android中的坐标偏移分析与问题解决

今天,就我在初学android中遇到的坐标偏移问题进行分析.
在初学android过程中, 自己根据api文档写了个画画板的小demo, 发布到不同的模拟器上会出现鼠标和画笔不是同一坐标的问题, 如下图所示:
320*480的模拟器
480*800的模拟器

对于为什么会出现这个问题, 经过查阅资料发现,是跟模拟器的像素密度有关.

首先, 来看看使用320*480的模拟器时, 我们在布局文件中设置的imageview的width和height与imageview控件加载完成后所获取到的控件宽高的值.
这里写图片描述
这里写图片描述
从结果上看,这两者是完全一样的, 也就是说控件的加载到手机屏幕时的像素点数量与手机本身的像素点数是1:1的关系.

再来看使用480*800的模拟器时,我们使用同样的imageview的width和height, 等到控件加载完成后, 获取到的实际宽高.
这里写图片描述
从结果中看到, imageView加载完成后, 控件的实际宽高变成了480和611. 与我们在布局文件中设置的320和407不一样, 他们相差了1.5倍.

这里是由于屏幕的像素密度不同而导致的.
而最主要的关键点是我们在activity中创建的bitmap所指定的width和height,
这里写图片描述
这里把bitmap的width和height设置为320和407, 实际上这是一个固定的值, 因为这里的参数只是纯数值并没有单位, 所以不是320dp*407dp, 当这个bitmap被加载到手机屏幕时, 320和407也就是它在手机屏幕上显示的像素点数. 所以发布到不同像素密度的手机屏幕时,bitmap都是320*407个像素点,是固定不变的, 当手机屏幕的像素密度越大, 单位尺寸内像素点数越多, 这里的差距就会越大, 相当于像素点被放大了. 如下图:
这里写图片描述
而鼠标的坐标点是按照屏幕的像素密度来获取的, 把这个坐标画到bitmap相对应的坐标上, 就是出现偏移的主要原因.

其实, 这里的坐标并没有偏移, 只是像素点被放大了, 看上去在对应的坐标点上画线时, 给人的错觉像是坐标偏移了.

这里写图片描述这里写图片描述
可以明显看出, 画笔粗细相同时, 画出来的线却不一样粗. 这也就验证了我上面所说
的两个像素点大小不一样. 因此在createdBitmap时,像素密度要和手机屏幕一样才行

坐标偏移的原因:

使用getWindowManager().getDefaultDisplay()获取手机屏幕的宽高320*480是分辨率,
也是指像素点数px, 并不是手机的物理尺寸. 而我们在写布局文件时, 定义的控件(例
如imageview)宽高的单位是dp, 在显示到手机屏幕时实际上是按像素点来显示的, 因
此我们获取控件的宽高width = iv_paint.getWidth();height = iv_paint.getHeight(); 获取
的也是该控件在手机屏幕中宽和高方向所占的像素个数.

而我们在定义bitmap时,
createBitmap = Bitmap.createBitmap(320, 407, Bitmap.Config.ARGB_8888);
这里的320, 和407 是像素点数, 是固定的.
与布局文件android:layout_width=”120dp“;android:layout_height=”207dp“
中的320dp和407dp 可能会有差别.

这种差别取决于你的手机屏幕像素密度. 如果你手机屏幕的像素密度dpi是160,
那么这两地方的数值是一样的, 如果你的手机屏幕像素密度dpi是240, 那么你的
控件会占 宽:320*(240/160)=480像素点, 高:407*(240/160)=611像素点

而你的bitmap却还是320*407像素点.

要注意 :这320*407像素点, 与控件480*611像素点所占的物理尺寸是一样的. 就是说
控件的像素密度是与手机一样的, 而创建的bitmap像素密度小了.

Density-independent pixel (dp)独立像素密度。标准是160dip.即1dp对应1个pixel,计
算公式如:px = dp * (dpi / 160),屏幕密度越大,1dp对应 的像素点越多。 上面的公式中有个dpi,dpi为DPI是Dots Per Inch(每英寸所打印的点数),也就是
当设备的dpi为160的时候1px=1dp;

像素密度:
假设有一部手机,屏幕的物理尺寸为1.5英寸x 2英寸, 屏幕分辨率为240x320, 则我们
可以计算出这部手机屏幕上, 每英寸包含的像素点的数量为240/1.5=160dpi (横向)
或320/2=160dpi(纵向), 160dip就是像素密度.

Android系统中定义了四种像素密度: 低(120dpi), 中(160dpi), 高(240dpi), 超高(320dpi),
它们对应的dp到px的系数分别为0.75, 1, 1.5 和 2, 这个系数 x 设置的dp数就是像素.

下面来看看如何解决这样的问题.

解决方案:

可以说有三种方案,
第一种: 比较笨重, 不能适配所有不同像素密度的屏幕, 只能适配当前的手机
在mainacitivy中, 创建一个子线程, 在子线程中去获取你的imageview控件的width和
Height,. 这种方法要先运行一次工程到手机, 获取之后就可以把子线程干掉啦.

代码:

New thread(){

    //这里一定要睡一下, 等imageview加载完成
    SystemClock.sleep(250);

    width = iv.getWidth();
    height = iv.getHeight();

}.start();

//把with和height填入

createBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

第二种:
就是使用监听器, 监听控件是否加载完成. 当加载完成后, 获取控件的width和height, 再
去画.
这种方法的好处是, 控件的width和height可以定义成matchParent

iv_paint.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

@Override
public void onGlobalLayout() {

width = iv_paint.getWidth();
height = iv_paint.getHeight();

/*// 5.2步: 把空画纸适配手机屏幕的宽高
createBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
…..
//用完要解除监听
iv_paint.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});

第三种:
适用于指定width和height的控件, 不需要监听,也不用子线程. 只需根据前面讲解的获取
当前手机的像素密度系数即可. 在createdBitmap时把指定width和heigh 都乘以这个像
素密度系数.

获取像素密度系数的代码:(在activity的oncreat方法中执行)

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
//获取像素密度
mDensity = metrics.density;

DisplayMetrics也可以获取手机屏幕的width和height(都是像素数)

createBitmap = Bitmap.createBitmap((int) (320*mDensity), (int) (407*mDensity), 
Bitmap.Config.ARGB_8888);

对于一些低密度的手机, 可能还要在清单文件中设置多分辨率支持. 添加

<supports-screens  />节点
<supports-screens android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:resizeable="true"
        android:anyDensity="true"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值