Android屏幕适配全攻略
一、碎片化
说起来,人们口中的 Android 碎片化主要表现在 Android 品牌和机型众多,Android 版本众多和 Android 设备的尺寸和分辨率众多。也就是Android碎片化并不局限于手机屏幕。本文仅限于与大家一起探讨屏幕适配相关内容。
来看一张流传甚广的图片:
上图每一个方框代表一种 Android 设备的屏幕,颜色越深,这种尺寸的屏幕也就越多。也就是说,Android 开发者理论上需要适配上图中的屏幕。
然而Android屏幕碎片并没有大多数人想象的那样令人头疼。Google 早已经给了我们界面视图布局工具,你可以自定义一种或多种界面视图,以适应不同尺寸的设备。如果你还想更精益求精,那么你还可以只保留一种代码库而尽可能多的调试更多的视图。
看到上边的统计图不知大家是否有深深的同情呢?其实直到目前为止市面最为主流的仍然是几款手机屏幕分辨率:
720 x 1280
768 x 1280
800 x 1280
1080 x 1920
1440 x 2560
实际上开发者大多数情况对这些主流的设备进行适配,所以不同屏幕进行适配比想象中简单很多。不过这也仅限于 Android 手机,Android 平板并未计算在内。本文将会与大家共同讨论下屏幕相关概念以及解决方案。
适当舍弃也是对抗 Android 碎片化重要武器。
二、屏幕适配概论
屏幕适配主要解决手机屏幕和像素多样化的问题,也就是我们的App能够适应各种各样的手机屏幕。首先我们要先了解屏幕相关的概念,如果该部分相关概念不清楚将会很难理解这部分问题。
三、什么是屏幕尺寸、屏幕分辨率、屏幕像素密度
1)屏幕尺寸
是指屏幕对角线的长度,单位英寸。1英寸 = 2.54cm。
2)屏幕分辨率
是指屏幕横向和纵向像素点数,单位px,1px = 1物理像素
一般以纵向像素*横向像素:1920 * 1080
3)像素密度
是指每英寸上的像素点数、单位dpi(dot per inch)的缩写、与像素无关的单位。
像素密度与屏幕尺寸、屏幕分辨率有直接的关系。
像素密度计算公式:sqrt(1920^2 + 1080^2) / 屏幕尺寸 (三角函数--勾股定理)
例:
1920^2 = 3686400
1080 ^ 2 = 1166400
3686400 + 1166400 = 48528004852800开方 = 2202.9071700822983
屏幕像素密度:2202 / 5.2 约等于424dpi 既:1英寸上有424个像素点(px)
像素密度是清晰度很重要的一个概念:即像素密度越高(dpi)代表显示屏能够以更高的密度显示图像。当然,显示的密度越高,拟真度就越高。dot per inch是图像分辨率的单位,图像dpi值越高,单位面积的像素数量就越多,所以画面的细节就越丰富。
四、什么是dp,dip,sp,pt,px及他们之间的关系
1、px
是构成图像的最小单位,也就是1px就相当于屏幕上的一个物理像素点。
2、dp、dpi、dip
是与密度无关的像素,主要是会有缩写方式不同、三者实际表示同一概念。后面统一用dp来代替。
3、dp与px的关系
Android规定:以160dp为基准,1dp = 1px。既像素密度为160dp时,此时1dp=1px,在Android中要彻底明白dp与px之间的关系才是理解适配的本质。
例:
分辨率 像素密度 dp = px
480 * 320 160dp 1dp = 1px
800 * 480 240dp 1dp = 1.5px
也就是说首先要搞清楚当前设备的像素密度,才可计算像素密度与像素之间的关系。
4、dp与屏幕适配
如下图所示:(一个240px * 160px的元素)
当使用px为单位:在480*320的屏幕上显示效果,占用屏幕宽度的1/2。此时将该尺寸放在800*480屏幕上显示效果变为屏幕宽度的1/3了。
如下图所示:
当将单位改为dp时,在480*320的屏幕上显示效果:占用屏幕宽的1/2,这是因为此时1dp=1px(故:160px)。此时将该尺寸放在800*480屏幕上显示效果也是屏幕的1/2,这是因为此时1dp = 1.5px(故:240px)。
5、sp
(scaled pixels)主要用于文字大小、可以根据文字大小首选项进行缩放。效果与dp类似,在使用时要注意Google推荐以偶数为单位,如果是奇数会影响精度问题。
6、pt
1pt = (1/72)英寸,既(2.54/72,单位cm),Android中并不推荐使用该单位。
在Android中为保证UI在不同屏幕类型上显示,应该始终保持:文字大小首选使用sp为单位,dp作为其他元素的单位。另外也可以考虑矢量图,而不是位图。
五、什么是mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi及其如何计算和区分
先来看张图,资源目录对应的像素密度范围:
再来看项目工程资源目录:
通过对照,此时每个资源目录下所对应的关系是不是一目了然了呢。其实Android系统就是根据目标手机的像素密度到对应的资源目录下去查找相关资源。这就是Android屏幕适配的原理。
1、mdpi
120dp ~ 160dp 此时 dp与px之间的关系:1dp = 0.75 ~ 1px
2、hdpi
160dp ~ 240dp 此时dp与px之间的关系:1dp = 1 ~ 1.5px
3、xhdpi
240dp ~ 320dp 此时dp与px之间的关系:1dp = 1.5 ~ 2px
4、xxhdpi
320dp ~ 480dp 此时dp与px之间的关系:1dp = 2 ~ 3px
5、xxxhdpi
480dp ~ 640dp 此时dp与px之间的关系:1dp = 3 ~ 4px
但是在实际开发过程中由于考虑到包大小,我们一般不会选择每个资源目录都提供(包会变得很大),比如我们只提供了一套资源:xxhdpi(320dp ~ 480dp),如果此时我们的App安装到目标为xhdpi(240dp ~ 320dp)设备上,Android系统是如何处理的呢?其实这又涉及到Android系统中加载一张资源图片最终占用的内存大小?由于不是我们本篇要讨论内容,不展开叙述,在这里直接公布答案:
系统会根据:目标的像素密度/资源所在的像素密度,得到一个值,然后根据这个值对图片资源宽高做缩放处理,以便达到最佳显示效果。
荐:Drawable微技巧
六、适配不同尺寸屏幕之wrap_content、match_parent、weight
其实上边说了那么多,仅仅对屏幕适配涉及到的相关概念做一个说明,接下来才是实战解决方案:
1、支持不同的屏幕尺寸
尽量使用wrap_content、match_parent、weight(权重),避免固定尺寸值。
wrap_content与match_parent相信大家都已经用过很多了,这里只着重说明下weight。首先weight只能使用在LinearLayout中。
我们通过一个实例来说明weight的作用,如下图:
布局显示如下:
此时我们作调整:TextView1占权重3,TextView2占权重
布局显示如下:
按照权重,我们将父布局分成4份,TextView1占3份,TextView2占1份,但实际效果却不是,这是为什么?
实际上weight属性计算规则是:首先按照控件自身的尺寸进行分配,然后将剩余的尺寸再按照权重进行分配。
我们的父布局宽度 = 200dp,TextView1宽度 = 50dp,TextView2宽度=50dp,两个控件共占用100dp,然后再将剩余尺寸按照权重划分(200 - 50 - 50)/ 4 = 25
此时:TextView1的实际宽度 = 50 + 25 * 3 = 125
TextView2的实际宽度 = 50 + 25 * 1 = 75
计算公式:控件本身宽度 + (父布局宽度 - 所有组件宽度之和)* 当前控件权重/总权重(当前例子:50 + (200 - 50 - 50) * 1/4 = 75)
2、RelativeLayout
这个相信大家都已经很熟悉了,主要它基于控件之间相对位置,在不同的手机屏幕上控件之间的相对位置不会发生变化。
七、适配不同尺寸屏幕之自动拉伸位图.9.PNG图片的使用
如何使用自动拉伸的位图:.9图(学名:Nine-Patch)
1、直接通过设计人员设计出.9图
这种方式就不过多叙述了。
2、通过Android Studio来完成一张普通图片到.9图
首先我们先把一张图片通过重命名修改成.9图片
然后通过Android Studio自带编辑器预览:
其实.9图的制作就是两部分:
1、指定哪一部分可以被拉伸。
2、指定哪一部分不可以被拉伸。
当我们把鼠标放在图片上,在图片四周会有一个宽度为1px的像素点,这个像素点就是控制哪些部分是可以被拉伸,哪些是不可以被拉伸的。感兴趣可以自己去体会下,但是大部分情况下一般由设计人员直接提供(这是个精细活,需要慢慢调)。
八、适配不同尺寸屏幕之限定符处理,手机与平板适配
终于到最后了,屏幕适配之限定符
1、最小宽度限定符
Android3.2以后,可以通过使用最小宽度限定符来为不同屏幕提供精确的布局。使用方式:如layout-sw600dp, values-sw600dp。这里的sw代表Small-Width(设备上最小的一边)的意思,当目标屏幕的最小宽度都大于480dp时,屏幕就会自动到带sw480dp后缀的资源文件里去寻找相关资源文件,这里的最小宽度是指屏幕宽高的较小值,每个屏幕都是固定的,不会随着屏幕横向纵向改变而改变。
如下图分别提供160dp~ 820dp的values资源:
v如下图分别提供7英寸平板与10英寸平板的layout资源:
如果是电视机要写到更大的值:如layout-sw1000dp,layout-sw2000dp
Android3.2之前?
使用尺寸限定符layout-large/???,values-large/???由于目前3.2之前的设备微乎其微,故不再展开叙述。
2、屏幕方向限定符
实际开发过程中这种需求较少。
values-sw600dp-land(最小宽度600dp横向)、values-sw600dp-port(最小宽度600dp纵向)
3、布局别名
如果我们仅建立一个layout资源目录,此时可以通过在values目录下定义layout.xml文件,如下图:
此时注意name要相同如下图:
系统会自动根据Small-Width到指定目录下查找,此时他们对应的布局文件并不相同,从而达到布局动态选择。
九、总结
1、适配不同尺寸避免写死的尺寸值;wrap_content,match_parent,weight。
2、使用相对位置的RelativeLayout。
3、考虑使用自动拉伸.9图
4、限定符;最小宽度限定符,屏幕方向限定符,布局别名。
相信如果你能够熟练运用这些进行界面搭建,就如同文章开头说到,屏幕适配并没有想想中的那么困难。
由于本人能力有限,欢迎您的指正。