被设计师支配的恐惧
很多的入门程序猿,对于android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路。回想还是一个入门菜鸟时,每当设计提出一个比较炫酷的动画设计,都会打开百度或者github先搜索一番。可是开源控件毕竟不是量身打造,与期望的效果总是有那么多的出入。这个时候就假装一本正经的说,这个没法实现。然后战战兢兢的等待设计那一句:“为什么ios的可以实现“?
磨刀不误砍柴工
android开发最让人头疼的是什么?我想应该是适配各种机型。android不像ios般统一,2012年到2014年支持Android设备的种类就从3997增长到18796。同时各大厂商定制的屏幕尺寸也非常多。这导致有时候我们在一款机型上表现完美的自定义控件,在另外一款手机上变形严重。所以在正式开始自定义控件之前,我们先回顾一下android中的各种尺寸
什么是屏幕尺寸、屏幕分辨率、屏幕像素密度?
我的测试机是三星S5,屏幕为5.2英寸。看一下手机参数:
拿它举个栗子,代码如下
DisplayMetricsmetrics = getResources().getDisplayMetrics();
int w = metrics.widthPixels;//宽度
int h = metrics.heightPixels;//高度
int dpi = metrics.densityDpi;//每英寸所占像素 (dpi:dots per inch )
int xdpi = metrics.xdpi;//x方向上的dpi
float density = metrics.density;//密度
Log.i("dimension","宽度:"+w+"\n高度:"+h+"\nDPI:"+dpi+"\nxDPI:"+xdpi+"\n密度:"+density);
log输出
dimension: 宽度:1080
高度:1920
DPI:480
xDPI:422.03
密度:3.0
DPI每英寸点数
可以看到三星S5的宽1080px,高1920px。根据勾股定理可以算出对角线长2202.907px。
因为手机对角线尺寸为5.2英寸,我们反向推导一下:
2202.907/5.2 = 423.6
与上面的xdpi相吻合。
所以dpi的概念一目了然,即每寸的像素有多少个点。面试的时候经常有同学把dpi、dip、dp弄混了。下面我接着推。
密度density
我们首先知道,谷歌官方把android设备的参考标准定义为一寸是160px。既然谷歌的一寸是160px,为什么metrics返回的dpi是480呢?。这是因为android设备中160px每寸的密度随着用户需求的提高,渐渐无法满足日常需要。所以厂商将原本160个像素提高到480个像素(在该例中,实际为422)。在同样的空间下密度增大了三倍。所以480像素得到了解释。由此我们又得到一个概念叫密度(density)
480/160 = 3
与上面的density相吻合。
独立像素密度(密度无关像素)DIP/DP
dp可以说是我们日常开发中最常用到的长度单位了。通过上面的计算我们可以得出。
1dp = 1英寸/160
当密度(density)为1时,1dp = 1px。
当密度(density)为2时,1dp = 2px。
当密度(density)为3时,1dp = 3px。
所以计算dp与px的转换公式也十分简单:
px = dp * density
或px = dp * (dpi/160)
常用单位一览
px——屏幕上真实的像素。这是一个与像素密度有关联的单位,一px单位的物理大小取决于屏幕的像素密度。
in——屏幕上的物理英寸。这是一个与像素密度无关联的单位,一in单位的物理大小在任何像素密度的屏幕上都是一样大的。一in单位转化为多少px单位取决于屏幕的像素密度。
dp——像素密度无关联的像素单位。这是一个与像素密度无关联的单位。然而一dp单位的物理大小在不同的像素密度屏幕上只是近视的相等。大约160dp等于一in。在一dp转化为160dpi中的一个比例因子是与设备的密度级别相关联的。一dp等于多少像素取决于屏幕的像素密度和设备所属的密度级别。
sp——大小独立的像素单位,特地指定text的大小。这是一个与像素密度无关联的单位。然而一sp单位的物理大小在不同的像素密度屏幕上只是近视的相等。在一sp转化为160dpi中的一个比例因子是与设备的密度级别以及字体表现的大小相关联的。一sp等于多少像素取决于屏幕的像素密度和设备所属的密度级别。
除此之外,android还为我们提供了一些其他不常用的长度单位
mm——屏幕上的物理毫米。这是一个与像素密度无关联的单位,一mm单位的物理大小在任何像素密度的屏幕上都是一样大的。25.4mm等于一in。一mm单位转化为多少px单位取决于屏幕的像素密度。
pt——点,屏幕上普通字体大小单位。这是一个与像素密度无关联的单位,一pt单位的物理大小在任何像素密度的屏幕上都是一样大的。72pt等于一in。一pt单位转化为多少px单位取决于屏幕的像素密度。
特别注意
事实上,我们屏幕上看到的东西都是由Paint绘制而来。而Paint对象接受的参数最后都是px像素来使用的。所以无论我们使用什么单位最后都转换成了像素来处理。
所以android系统很贴心的给我们提供了TypedValue.applyDimension()方法提供单位转换。点进去可以看到:
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}```
事实上还是```px = dp * density```公式的变形。有时候我们发现自定义View通过java代码设置尺寸就是不能适配,但是通过属性就可以。这里其实就是忘记调用TypedValue.applyDimension( unit, size, r.getDisplayMetrics())方法来把单位转换成px
### 结语
好了,虽然内容不多。但是写文章还是比我想象中困难很多。之前只是脑海里有这样一个概念。但是要组织语言写出来还是有障碍。再接再厉