Android屏幕适配——多分辨率多屏幕密度
为什么要适配,适配的好处等等这里就不说了,直接说我们要怎么适配,请看下面的内容。
1.重要概念
px:pixel,像素Android原生API,UI设计计量单位,如获取屏幕宽高。
屏幕分辨率:指在纵向和横向方向上的像素点数,单位是px,一般显示方式是纵向像素数量*横向像素数量,如1920*1080。
屏幕尺寸:一般是屏幕对角线长度,单位是英寸,常见尺寸有3.5,4.0,4.3,4.7,5.0,6.0等。
屏幕像素密度:ppi pixel per inch的缩写,意思是每英寸屏幕上的像素数,因为屏幕尺寸是商家生产时就规定好的,屏幕尺寸一样的手机,屏幕宽高却不一定一样,所以通常取屏幕对角线像素数量和屏幕尺寸(屏幕对角线长度)来计算屏幕像素密度,计算公式就是通过勾股定理和分辨率计算得到屏幕对角线像素数量,再除以屏幕尺寸。手机参数上也会有这个数值。
dpi:dot per inch点像素密度,它的计算方法也和ppi一样,但从很多方面上,dpi是和ppi有区别的,ppi是用在设备上的单位,比如显示器;而dpi是用在印刷品上的单位,比如要打印一幅地图;在图像上的像素看起来是一个个点或者方块,这时候通常就将两者混用。要注意的是,我们并不能在Android适配时直接使用手机参数上的ppi值,而是使用dpi,Android对dpi根据大小做了规定的分档,以160dpi为基准,分为ldpi(120dpi)、mdpi(160dpi)、hdpi(240dpi)、xhdpi(320dpi)、xxhdpi(480dpi)等,当然现在手机的分辨率和尺寸更加多,像560dpi,600dpi也有,举个例子,Nexus 6 ppi是493,那按道理我们计算出来的dpi也是493,但Android的分档里面并没有493dpi,所以实际上它的dpi是560,而这个值我们是可以在手机的系统文件里面修改的。一定程度上,我们甚至可以说分辨率和适配没有关系,我们需要考虑的是dpi值,而这个值是可以与分辨率没有关系的,是手机厂家设的。
dp/dip:density independent pixels密度无关像素,Android设计的一个单位,它与px存在一个换算关系,Android规定,在160dpi时,1dp等于1px,那么320dpi时,1dp就等于2px,
所以当px换算为dp时,dp =160 * px / dpi;dp换算为px时,px = dp * dpi /160。换算方法代码:
px与dp的相互换算sp:scale independent pixels比例无关像素,是Android推荐使用的字体大小单位,同样是密度无关,而且看上去似乎使用dp也没问题(以前的Android开发很多是这样写)但是,原因就在比例上,字体除了数值设置外,还受到系统字体大小设置的影响,而使用sp是适应这种变化的,dp则不会跟随系统字体大小设置的变化而变化,所以除非你想字体大小不随着系统字体大小设置变化,字体大小单位都使用sp。但是sp与设计图上px的转换可以使用dp与px的换算公式。
2.适配
适配方案有很多种,比如百分比布局,比如根据每个要适配的屏幕都写一套布局,比如分辨率等比缩放等。但都有这样那样的问题,Apk体积增大,难以维护……我个人比较推荐以下的做法:
多个demins(dp写法)
我们可以把控件的大小数值写在多个dimens xml文件中,让应用显示控件大小的时候像获取图片资源一样,根据规则选择具体的值。而dimens文件在values文件夹下,其实就是应用到不同的values文件夹下的dimens文件中读取到需要的值。但这个“规则”的设置就是最考验适配技巧的时候了,诀窍在于我们怎么去命名values文件夹和对设计稿上控件大小进行符合“规则”的单位换算。让我们来看一下怎么去做?
values文件夹根据限定符去命名
屏幕尺寸:指最短的可用屏幕区域。如sw480dp,表示适配可用屏幕尺寸起码为480dp的情况。
屏幕分辨率:必须较大数值在前,较小数值在后,如1920x1080,表示适配分辨率为1920x1080的情况,其中x是英文字母x而不是乘号。
屏幕像素密度:如mdpi,表示适配屏幕像素密度值近似或等于mdpi(160dpi)的情况。
屏幕方向:如land(横向)、port(竖向)表示适配屏幕方向为land或者port的情况。
版本:vX(X代表版本号,现在是1到23),表示适配SDK版本为X的情况。
还有很多这里就不一一细说了。
限定符可以组合起来使用来限定更精确的情况如:values-mdpi-land-1920×1080-v19,表示适配SDK版本为19,分辨率为1920×1080的横向屏幕的mdpi手机。
学过组合的都知道这里能有多少种限定符组合,而且其中分辨率的值还是不定的,只要有厂家生产不同分辨率的手机,我们就可以有无数种限定符组合,然而,我们并不用惧怕,限定符的适配是向下匹配的,譬如:有values-port-1920x1080的手机屏幕需要适配,当我们建立的values文件夹里面没有一样的时候,就适配到下一级,比如values-port-1280x720(如果有而且是最接近目标的话),如果还没有,一直向下,最后起码能适配到values这个没有限定符的。
这里现在只考虑竖屏的时候(横屏就是增加land限定符,而且数值变化太大,布局也可能需要专门去写,适配的原理还是一样的,这里就以竖屏为例)根据以下的例子:
1.UI给我们做了一套1920x1080的图片,通常将该分辨率的图片放在drawable-xxhdpi的文件夹下,应为通常分辨率为1920x1080的屏幕都是xxhdpi(480dpi)的。
2.这时候,图片上有一个90x30(px)的控件,在1920x1080的机子的布局里面根据换算方法转换成30x10(dp)而xhdpi(320dpi)的1280x720上是多少呢?设在该屏幕上的长为A像素(px),根据比例式,A/720=30/1080,那么就知道A为20px,那么根据换算公式,dp还是10!
3.我们再来看看两个机子它们的sw是多少dp?选短边计算,1080/480/160=360,720/320/160=360,那么结合第二点来看,只要sw相同,无论具体的在屏幕上显示的px是多少,dp都是相同的,dp相同,代表视觉大小是相同的,意味着我们看到的布局效果是一样的,适配解决!
所以我们只需要使用规则给values文件夹命名如下:values-swXXXdp,就可以了。大概像这样:
values文件夹命名可能有人有疑问了,那么如果遇上一些超级奇葩的厂家给机子设置了很奇葩的dpi值呢,单靠这个规则就真的OK吗?
譬如:遇上分辨率为1980x1080,但dpi是mdpi的屏幕时,这台机子的尺寸就是短边1080/160/160=1080dp,那么我们只需要建立一个values-sw1080dp的文件夹,具体的dp值就是控件长的px值B = 30/1080*1080 = 30,dp = 30 / (160/160 )= 30,这并没有什么问题。
其实可以看出来,这不是分辨率等比缩放的写法吗?只不过把等比缩放之后的分辨率再根据换算方法将px换成dp罢了。但是!分辨率可以有无穷多种,而以下是可以使用的通用屏幕尺寸的一些值:
1.320,针对以下屏幕配置的设备:
240x320ldpi(QVGA手持设备)
320x480mdpi(手持设备)
480x800hdpi(高分辨率手持设备)
2.480,针对480x800mdpi的屏幕(平板或手持设备)
3.600,针对600x1024mdip的屏幕(7英寸平板)
4.720,针对720x1280mdip的屏幕(10英寸平板)
注意:当应用程序提供了多个带有不同值的最小宽度限定符资源目录时,系统会使用最接近(不超出)设备最小宽度的那个资源。这个限定符被添加在API级别13中。还要看android:requiresSmallestWidthDp属性,它声明了与你的应用程序兼容的最小的最小宽度,并且smallestScreenWidthDp配置字段会持有这个设备最小宽度的值。
那么我们的灵活性就大大提高的同时,相对于少则十几多则数十的分辨率种类,会使用近似值的sw-XXXdp写法就只需要少于十种的就可以搞定了。
当然,按照前面说的限定符规则,在sw-XXXdp的基础上,我们还可以加上版本号,屏幕方向去针对某些系统版本的手机或者平板,同样是比使用分辨率的少很多。
最后的问题就是就算要处理的只有几个dimens文件,但每个里面可能有十分多的值,一个个换算,新建,修改,简直是折磨人的,但既然我们知道了换算的规则,知道了dimens的调用规则,写个方法去修改xml的文件内容是很简单的,代码请下载(这里要感谢柯铿!):Android屏幕适配