UI适配方案
Android的ui适配一般有两种解决方案,一种是通过系统的资源适配机制(layout和drawable),另一种是通过代码来适配(fragment和view),两种方案也经常结合起来用。
1 资源适配
Android的资源适配机制的原理就是在一个app工程中为不同分辨率,不同尺寸和不同方向的屏幕准备相对应的layout文件和图片,在运行时系统会自动找到相应的文件,而要使系统正确地找到最优的资源文件,开发者必须遵守android的资源适配规则。
1.1 需掌握的概念
1.11 Screen size(屏幕尺寸)
android定义了small,
normal, large, xlarge四种尺寸,其中normal是以G1
3.5寸的屏幕为基准,一种尺寸标准一般对应一个范围的屏幕大小,具体如下:
Android 3.2(level
13)之后,新加入了最小dp的概念,有swdp,wdp,hdp,n代表最小的数值,其中wdp指屏幕宽度>=ndp,hdp指屏幕高度>=ndp,swdp指高度和宽度都要>=ndp,比如w600dp意味着当运行在宽度>=600dp的屏幕上时才会加载此级别的资源文件。
1.12 Orientation(屏幕方向)
Android定义了port(横向),land(纵向)两种方向,如果应用支持app横向模式,一般都会针对layout提供一个land方向的资源文件,以对layout重新布局。
1.13 Density-independent pixel (dp)
dp是用来衡量密度和像素之间关系的单位,以mdpi(160dpi)为基准,1dp=1px,其他按比例变化,比如hdpi(240dpi)中,1dp=1.5px。dp主要用来设置view的大小,如果用px来设置view的大小,在相同大小的屏幕上,高分辨率的屏幕下view的物理大小会变小(因为一个像素的物理大小变小了),而如果使用dp,比如高度为100dp,则view的大小几乎没有变化(因为虽然一个像素变小了,但在高分辨率的屏幕上,100dp>100px,最终像素数量变多了,物理大小=像素大小*像素数量,相互抵消后,view的物理大小几乎不变),也就是说,只要我们使用dp,然后在mdpi的基准下调整一个最佳的数值,在所有设备上都可以呈现相同的物理大小。
1.14 Scale-independent pixel (sp)
sp的原理和dp类似,使用在字体大小的设置上,除了受屏幕的参数影响外,还受系统字体设置的影响(很多机型没有这个设置功能),当把系统字体的大小设置更大时,字体也会在用来的基础上变大。
以上的几个概念都应用在layout上,下面的Screen
density应用在drawable上。
1.15 Screen density(屏幕密度)
Android定义了low,
medium, high, extra-high, extra-extra-high,
extra-extra-extra-high 6种密度,简写为ldpi(120dpi),
mdpi(160dpi), hdpi,(240dpi), xhdpi(320dpi), xxhdpi(480dpi),
xxxhdpi(640dpi),
主要用来衡量一定的屏幕面积内像素的数量,和我们平时说的分辨率相似,在屏幕大小相同时,屏幕密度越高,分辨率也越高,而为了使图片不失真,所需要的图片分辨率也要越高,这就是android适配图片的基础。
1.2 layout
layout适配要解决的问题一般有两种,一种是布局的变化,比如在大屏幕上或者横向屏幕上我们希望布局有所变化来充分利用屏幕的空间,另一种是view的大小适配问题,我们要避免view会出现过大或过小的情况。
1.21 布局的变化适配
在android项目工程中,通过在不同的资源文件夹放入相应的layout文件,系统会自动帮我们找到适应的layout,如下:
文件夹的命名方式需遵守android的命名规范,通过IDE的提示可完成。
Android这套机制最适合的场景就是在小的屏幕下(比如手机),布局是一个列表,而在大的屏幕下(平板)或者横向屏幕下,布局除了显示一个列表,还显示详细信息,如下:
要实现这种适配,我们只需定义两个布局文件(single-pane和multi-pane),并取相同的文件名,放到相应的文件夹就行,如下:
一般还会结合fragment来实现这种场景,会更加灵活,封装性也更好。
附注:在layout中,google建议不要用AbsoluteLayout。
1.22 View的大小适配
在设置view的宽度和高度时,不要用px作为单位,而是用dp和sp(字体),或者是wrap_content,match_parent这种自动适应单位,常用的还有按比例分配大小。
1.221 dp和sp
在用dp设置view的大小时,一般在mdpi的屏幕上调节一个最佳的数值,再用dp作为单位,sp原理一样,只是用于字体大小的设置。
1.222 wrap_content
wrap_content主要适用于对view的大小没有要求,只是希望能完整的显示出内容,或者内容是动态变化的,比如textview或button的字体内容。wrap_content由于需要计算出内容的大小,所以相比match_parent,wrap_content更加耗时。
1.223 match_parent
match_parent主要适用于view的parent已经确定好大小,而view需要占满整个parent。
1.224 按比例
按比例分配大小主要适用于parent的大小已经确定,多个子view大小要按比例分配,要实现按比例分配,一种是直接在代码获取parent的大小,再按比例计算出子view的大小,另一种是使用LinearLayout作为parent,子view的大小设为0,通过设置weight的值来设置比例。
1.3 drawable
drawable的适配也是通过资源文件夹来分配的,如下:
1.31 drawable分辨率
图片的分辨率比例为ldpi : mdpi
: hdpi : xhdpi : xxhdpi : xxxhdpi =
3:4:6:8:12:16,比如在设置启动图标时,mdpi的分辨率为48 * 48,其他的则按比例调整。
1.32 资源查找及项目实践
在实际项目中,一般不会准备所有分辨率的图片,因为在当前density的资源文件夹找不到相应的资源时,系统会到其他的文件夹找,再做相应的比例缩放,根据google官方文档,系统查找的顺序并不确定,一般会到默认的资源文件夹(没有dpi后缀)找,但如果当前屏幕是ldpi的,系统会优先使用hdpi而不是mdpi的,因为hdpi到ldpi要缩小0.5倍,而mdpi到ldpi需要0.75倍,缩小0.5倍效率更高。
在之前的测试中(android
4.4),发现系统是从最高density文件夹往低找,同时结合缩放比例来确定查找结果,这和layout缺失时有点不同,layout的查找顺序是从当前size往低找,因为对于layout来说,可以兼容小的,但更大的layout可能就会出问题了(如果只有更大级别的layout,系统会报错),而对于drawable来说,分辨率更大,缩放后图片也不会失真。
因为系统的查找策略对开发者来说是透明的,所以在项目中,一般会为mdpi,hdpi,xhdpi
.......准备相应的图片资源,而对于高分辨率图片(背景图),则一般会准备一套,再由系统去缩放。
2 代码适配
为了符合MVC模式,并让界面和代码尽可能地分离,采用上述的资源适配方案更好,但在有些场景下却需要在代码里来适配ui,比如需要根据屏幕的宽度或高度来决定view的大小,或者在运行时需要动态调整view的大小。同时,fragment的引入也使xml和代码的融合更加灵活(但复杂性也相应变大)。
2.1 view
在代码中设置view的大小时最常用的一个参数就是density,在mdpi下density为1,因为设置view大小的api的参数是以像素为单位的,所以我们只需将mdpi下的基准数值乘以density就可以实现xml下dp的效果,
获取density的方法如下:
getResources().getDisplayMetrics().density
2.2 fragment
Fragment是android
3.0引入的,可以通过support
v4包来兼容3.0之前的版本。个人觉得fragment更像是一个容器,相比view,fragment除了可以绑定一个layout外,还有自己的生命周期,也可以管理自己的数据,更利于代码的封装,但复杂性就变大了。
一般来说fragment适用于动态布局的场景下,比如上述的双列表(平板)和单列表(手机),fragment可以直接在layout文件中作为view来声明,如下:
Fragment的具体用法请看开发者文档。
3 其他方案
3.1 support screen
通过在mianifest里加上标签可以声明应用支持或不支持多大的屏幕,google
play等商店也会通过此标签来判断app支持的屏幕尺寸,此标签常用于屏幕兼容模式中。
可设置的值如下:
请见:www.developer.android.com/guide/topics/manifest/supports-screens-element.html
3.2 Screen Compatibility Mode(屏幕兼容模式)
兼容模式的目的是为没有做任何UI适配的app在平板上运行提供一种UI优化,有两种版本的兼容模式,android
1.6之前是version 1,1.6之后是version 2,下面介绍的是基于version 2。
google官方文档不建议使用此功能,而是采用ui适配方案。
个人觉得目前此功能很少用,都是通过android的资源适配机制或者fragment来解决UI适配的问题。
3.21 开启兼容模式
当运行在android
3.2及以上的平板上时,以下情况下会让用户选择是否使用兼容模式:
1)android:minSdkVersion和android:targetSdkVersion
都设置为10以下,且没有设置来说明app支持大屏幕。
2)android:minSdkVersion和android:targetSdkVersion
都设置为11及以上,并且设置来说明没有适配大屏幕。
3.22 关闭兼容模式
目前targetSdkVersion
都大于11,所以只要没有设置就不会出现兼容模式。
详细信息见开发者文档:www.developer.android.com/guide/practices/screen-compat-mode.html
3.3 Nine-patch Bitmaps
Nine-patch
Bitmaps是android针对图片拉伸的一种优化技术,通过在一张普通的图片最外层相应位置加上像素点,可以使图片在拉伸时只拉伸像素点所在的方向,而其他位置(有形状的位置)则不会被拉伸导致变形。如下:
拉伸后:
可以通过sdk自带的draw9patch工具来生成Nine-patch
Bitmap,生成的图片后缀为xx.9.png。
参考文档:www.developer.android.com/training/multiscreen/index.html
www.developer.android.com/guide/practices/screens_support.html