1、概述
过去,程序员通常以像素为单位设计计算机用户界面。例如:图片大小为80×32像素。这样处理的问题在于,如果在一个每英寸点数(dpi)更高的新显示器上运行该程序,则用户界面会显得很小。在有些情况下,用户界面可能会小到难以看清内容。由此我们采用与分辨率无关的度量单位来开发程序就能够解决这个问题。Android应用开发支持不同的度量单位。
2、度量单位含义
· dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
· dp: dip是一样的。
· px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。
· pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用。
· sp: scaled pixels(放大像素). 主要用于字体显示best for textsize。
· in(英寸):长度单位。
· mm(毫米):长度单位。
3、度量单位的换算公式
在android源码包TypedValue.java中,我们看如下函数:
public static float applyDimension(int unit, float value, 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;
}
该函数功能:是把各单位换算为像素。
metrics.density:默认值为DENSITY_DEVICE / (float) DENSITY_DEFAULT;
metrics.scaledDensity:默认值为DENSITY_DEVICE / (float) DENSITY_DEFAULT;
metrics.xdpi:默认值为DENSITY_DEVICE;
DENSITY_DEVICE:为屏幕密度
DENSITY_DEFAULT:默认值为160
4、屏幕密度:表示每英寸有多少个显示点,与分辨率是两个不同的概念。
屏幕相关概念
1.1分辨率: 是指屏幕上有横竖各有多少个像素
1.2屏幕尺寸: 指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸
android将屏幕大小分为四个级别(small,normal,large,and extra large)。1.3屏幕密度: 每英寸像素数
手机可以有相同的分辨率,但屏幕尺寸可以不相同,Diagonal pixel表示对角线的像素值(=),DPI=933/3.7=252
android将实际的屏幕密度分为四个通用尺寸(low,medium,high,and extra high)
一般情况下的普通屏幕:ldpi是120dpi,mdpi是160dpi,hdpi是240dpi,xhdpi是320dpi,对于屏幕来说,dpi越大,屏幕的精细度越高,屏幕看起来就越清楚1.4密度无关的像素(Density-independent pixel——dip): dip是一种虚拟的像素单位
dip和具体像素值的对应公式是dip/pixel=dpi值/160,也就是px = dp * (dpi / 160)
当你定义应用的布局的UI时应该使用dp单位,确保UI在不同的屏幕上正确显示。
Android主要有以下几种屏:如下表
· VGA:Video Graphics Array,即:显示绘图矩阵,相当于640×480 像素;
· HVGA:Half-size VGA,即:VGA的一半,分辨率为480×320,像三星盖世Ace S5830就是使用这分辨率;
· QVGA:Quarter VGA,即:VGA的四分之一,分辨率为320×240,一般用于小屏手机 像三星盖世Mini S5570就是使用这分辨率;
· WQVGA:Wide Quarter VGA,即:扩大的QVGA,分辨率比QVGA高,比VGA低,一般是:400×240,480×272;
· WVGA:Wide Video Graphics Array,即:扩大的VGA,分辨率为800×480像素,像三星i9000就是使用这分辨率;
· FWVGA:Full Wide VGA ,数码产品屏幕材质的一种,VGA的另一种形式,比WVGA分辨率高,别名 : Full Wide VGA, ,其分辨 率为854×480象素(16:9)。
【扩展】以下是一些常见的分辨率
QVGA 320×240,WQVGA 400×240,VGA 640×480,WVGA 800×480,SVGA 800×600,WSVGA 1024×600,XGA 1024×768,WXGA 1280×768/1280×800/1280*960,SXGA 1280×1024,WXGA+1440×900,SXGA+1400×1050,WSXGA+1680×1050,UXGA 1600×1200,WUXGA 1920×1200,QXGA 2048×1536,WQXGA 2560×1536
ldpi 320*240 1dp = 0.75px
mdpi 480*320 1dp = 1px
hdpi 800*480 = 1.5px
xhdpi 1280*720 1dp = 2px
xxhdpi 1920*1080 1dp = 3px
1280(手机的高度上面分布了1280像素点)*720(手机的宽度上面分布了720个像素点) 5寸(斜对角线)
斜边分布的像素点
像素密度:在一寸的大小范围内分布多少个像素点
sqrt(1280*1280+720*720)/5 293.72 dpi 最接近320dpi 属于xhdpi范围,1dp = 2px 160dp = 320px 720px = 360px 180dp = 360px
800*480 4寸 160dp
sqrt(800*800+480*480)/4 = 233.23dpi 接近240dpi hdpi 1dp = 1.5px 160dp = 240px 480px
480dpi xxhdpi 1:3
综上所述:
1. 据 px = dip * density / 160,则当屏幕密度为160dpi时,px = dip。
2. 根据 google 的建议,TextView 的字号最好使用 sp 做单位,而且查看TextView的源码可知Android默认使用sp作为字号单位。将dip作为其他元素的单位。
3. 密度无关的像素dp(density-independent pixel),缩放比例无关的像素sp(scale-independent pixel) ;可分别定义视图和字号
5、屏幕适配
这个问题我相信困惑了很多人包括很多老鸟,而且有的人以为自己理解了其实是错的,当然不包括全部的人哈!不过还是有哪么些人绝壁理解错了,包括之前的我在内,一般让美工做720*1280的切图,就直接放到xhdpi下,如果是做了1080*1920,就直接放到xxhdpi下。其实这四个文件夹和具体的屏幕分辨率是没直接关系的,上面说的做法也有一定道理,因为大部分的720*1280的手机都是高密度手机,1080*1920都是超高密度手机,但是这四个文件夹之和屏幕密度有关,和具体分辨率无关。
下面给出一个最最关键的等式:
low:medium:high:extra-high:extra-extra-high=3:4:6:8:12
OK,就是说五个文件夹的比例为3:4:6:8:12。具体是怎么回事呢?下面分析一下:
比如我用一个480*800的4寸手机,这个手机的屏幕密度按照Google的说法,就属于密度为high level的水平(通过分辨率和屏幕尺寸计算密度,然后google自己有一套标准说你位于哪个范围属于哪个level的密度水平),然后这个手机的应用在用图片的时候,就会去hdpi下去找,并且以这个文件夹的图片为标准,也就是说比如我的应用去取一张aa.png的图片,这个图片的原图尺寸为30*30,恰好hdpi下有一张,那这张图片显示到屏幕上以后,它的显示尺寸长宽都为30px。那问题来了,但如果我的hdpi下没有这张图片,而只在xhdpi下有这张图片,图片的原图尺寸是30*30,那请问显示到屏幕上的图片的尺寸会是多大呢,还是长宽都为30px吗?
答案是否定的,而且现在就用到了上面那个比例,high:extra-high=6:8。先明确这样一个问题,如果我的屏幕是hdpi的,结果我的图片是放到了xhdpi下,那系统会把这张图片进行缩小显示,也就是说我的xhdpi下放了一张30*30的图片,那显示当hdpi屏幕上肯定要比30*30小,这样才能保证说大小屏幕界面显示效果是一致的,因为密度小的手机显示一张图片要比密度大的手机显示同一张图片的面积要大,要想显示面积一样就必须要把图片搞小点。接上面的问题,假设显示在我的hdpi屏幕上的图片的宽度为x,那满足以下等式:
6:8=x:30 。
可得到x=22.3,向后取整数得23 。
再来说说图片适配问题:
切图:
现在主流的屏幕分辨率有1920*1080 , 1280*720 , 800*480 , 480*320这两个。
所以要切图的话,那么就切1920*1080 , 1280*720 , 800*480分辨率的图片。
但是如果就切一套图片的话,那么就切800*480的图片,但是导航图片必须还是需要切两套的。其余的图片就按照800*480的图片进行切割。
一共有5种方式:
通过图片适配
通过布局适配(提供多套布局)
dp在一定程度上是可以适配的,但是不代表一定可以适配。
注意:
a 必须大数写在前面.
b 是X.dimens适配
通过代码
public class MainActivity extends Activity { private static final String tag = "MainActivity"; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.tv_text); //通过java代码让当前控件宽度占屏幕宽度的50%,高度占屏幕高度的20% //1,获取屏幕宽高,获取一个封装屏幕显示属性的对象 DisplayMetrics displayMetrics = new DisplayMetrics(); //给相应的对象赋值 getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); Constant.srceenWidth = displayMetrics.widthPixels; Constant.srceenHeight = displayMetrics.heightPixels; Log.i(tag, "Constant.srceenWidth ="+Constant.srceenWidth); Log.i(tag, "Constant.srceenHeight ="+Constant.srceenHeight); / /给当前的textView去设置一个显示的规则,规则是由父控件去定的,接收像素 LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( //从数学角度上来说,做到四舍五入 (int)(Constant.srceenWidth*0.5+0.5), (int)(Constant.srceenHeight*0.2+0.5) ); //将当前规则设置给指定控件 textView.setLayoutParams(layoutParams); } }
比例适配(权重适配)
注意:在这里必须是线性布局才可以的。
扩展:
http://stormzhang.com/android/2014/05/16/android-screen-adaptation/
http://www.iteye.com/topic/1128878