Android屏幕适配一直是Android开发们的一个痛点,各种各样的屏幕分辨率等,对Android的屏幕适配带来了很大的麻烦,而谷歌的解决方案也并不被所有人满意,所以笔者结合Android官方文档,来谈谈这个话题。
术语和基本概念
本节介绍Android屏幕单位等的一些基本概念。
屏幕尺寸
官方文档的解释是:
按屏幕对角测量的实际物理尺寸。 为简便起见,Android 将所有实际屏幕尺寸分组为四种通用尺寸:小、 正常、大和超大。
屏幕尺寸是指屏幕的物理尺寸大小,Android使用samll,normal,large,xlarge作为屏幕尺寸的限定符(3.2之后推荐使用sw<N>dp等来适配屏幕大小)。
屏幕密度
屏幕物理区域中的像素量,屏幕密度的单位是dpi(每英寸点数),粗略来说,屏幕密度越高的手机,看起来越清晰,我们通常放多组图片就是根据屏幕密度来划分的,Android将屏幕密度分为低、中、高、超高、超超高和超超超高,用字母表示是ldpi,mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi。
分辨率
分辨率是指屏幕宽高的像素值。
密度无关像素
官方文档的解释是:
在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度 或位置。
密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单: px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
典型手机的屏幕
先看dp和px的信息:
dpi | ?px=1dp | |
---|---|---|
ldpi | 120dpi | 0.75 |
mdpi | 160dpi | 1 |
hdpi | 2400dpi | 1.5 |
xhdpi | 320dpi | 2 |
xxhdpi | 480dpi | 3 |
xxxhdpi | 640dpi | 4 |
也就是说,屏幕像素密度越高的手机,1dp转换的像素数越大,成正比。 知道了dp的px转换,那么,Google引入dp来代替px究竟有什么用处呢?dp究竟是什么值呢? 看这个问题就要先看dpi,dpi是每英寸像素数,而dp与px的转换是与dpi成正比的,那么,对于一个dpi是x的手机来说,每英寸对应xpx,而对于这个手机来说,(x/160)px=1dp,那么每英寸对应x(160/x)dp=160dp。所以dp实际上反应的是实际的物理尺寸,和屏幕的分辨率,屏幕的像素密度等都无关。
Google推荐适配方案
了解了这些基本概念后,那么我们试着去理解Google的屏幕适配理念。首先,对用户来说,相同物理大小的屏幕肯定是一样的布局更好,而不该受限于屏幕的像素密度,所以Google用dp来解决这个问题,使用dp作为长度的单位,并配合Android的提供资源方案,能做到在相同物理大小的屏幕上有相同的布局。那么对于不同物理大小的屏幕,Google推荐对不同的屏幕进行专门的适配,而不是对布局进行简单的拉伸。
按比例适配方案
Google的方案固然是好,可是我们在做快速开发的时候,并不可能在屏幕的适配上下这么大的功夫,那有没有办法让所有屏幕的布局按比例看起来都是一样的呢。。。鸿洋的文章给出了一个这样的方案。大致就是,根据UI给的切图,通过Java代码生成values文件,把所有的像素值替换成其余屏幕上的像素值。 例如UI切图是480320的分辨率,那么: 宽度为320,将任何分辨率的宽度分为320份,取值为x1-x320; 高度为480,将任何分辨率的高度分为480份,取值为y1-y480。 对于800480的宽度480:
其余分辨率类似。那么现在如果我们对所有的分辨率都做这个处理: 然后布局的时候使用xn,yn做单位,就可以做到按照百分比去适配屏幕了。这么做是不是就完全没问题呢?
- 我们做头像的ImageView一般是正方形的,在上例中,UI给的切图可能是40*40像素的,我们就在布局中写x40*y40,可是其不不能保证在所有屏幕中都是正方形的。例如对于800*480的屏幕,x40=66.7px,y40=60px,这时候显示在屏幕上的就不是正方形的了。
- 使用px去适配是偏离Google的适配方案的,那么对于小屏幕高dpi的手机,Android会选择高精度图片,而由于屏幕小导致分辨率也不高,实际显示的像素可能低于选用的图片,造成内存的浪费(此时常规方式写的界面一般也会有适配问题)。也就是说Android的资源选取方案“不好用”了。
适配建议
以下是Google给的屏幕适配建议:
- 在 XML 布局文件中指定尺寸时使用 wrap_content、match_parent 或 dp 单位;
- 不要在应用代码中使用硬编码的像素值;
- 不要使用 AbsoluteLayout(已弃用);
- 为不同屏幕密度提供替代位图可绘制对象;
对于UI的切图,我们将其像素转换为dp写界面时,在xml文件中尽量不要使用较大的dp值,例如
我们写红色快的位置时,最好不要使用android:layout_marginRight="200dp"
,使用 android:layout_alignParentRight="true" android:layout_marginRight="30dp"
替代,因为200dp占屏幕的百分比更大,在不同屏幕下差异会比较大,对于宽400dp的屏幕,她左边对其50%的位置,对于宽600dp的屏幕,她左边对其67%的位置。当必须要使用大数值的dp时,如轮播图的高度等,可以对数值坐下屏幕适配。 写在最后的话
屏幕适配是个很繁杂的问题,可是一般也不用太受它困扰,我们只要使用dp去编写界面,就能满足大部分的适配要求,然后在针对测试出来的适配问题去专门适配就好。
参考