首页 > 代码库 > Android 之UI自适应解决方案
Android 之UI自适应解决方案
1.概况
作为Android开发人员,最头疼的莫过于让自己开发的程序在不同终端上面的显示效果看起来尽量一致(当然,如果要充分利用大屏幕的优势另当别论)。在全球范围内来讲,android有着数以亿计的设备,其中就不乏设备分辨率多种多样,以及设备屏幕物理尺寸的多样化。
总得来说我们需要做的有三点,其一让APP的每个UI中的每个View宽和高更加灵活以适应不同分辨率、其二对于大屏幕设备(PAD)需要有不同的设计,竟可能多的展示内容,获取你整个APP的所有UI都可以做到一个布局中来、其三图标资源需提供不同尺寸(MDPI、HDPI、XHDPI、XXHDPI、XXXHDPI),你也可以图方便只提供一套较高尺寸的API,没问题,这可能只会使那些配置低的设备(或者说分辨率低,其实实际这两者普遍成正比)耗费更多的内存在加载资源图片上面。
2.官方支持
官方建议APP中使用的图标资源都需提供drawable-ldpi(目前来看,已经没有必要)、drawable-mdpi、drawable-hdpi、drawable-xhdpi。
说到DPI(Dots Per Inch)其实就是单位尺寸有多少个像素,计算方式就是手机分辨率宽的平方+高的平方,再开根号得到斜对角线分辨率。然后用这个除以屏幕的实际尺寸(也就是我们常说的4寸、5寸、7寸、10寸),这个尺寸实际也是屏幕的斜对角尺寸。这样计算出来的结果就是像素密度,像素密度越大说明手机显示越细腻。举个例子HTC One分辨率是1920*1080,屏幕尺寸是4.7英寸,拿上面的公式计算一下2202/4.7=468那么它属于上面哪个区域呢?android给出了一个范围,如下:
很显然HTC One 被归入xhdpi这一类里面。所以如果你的APP提供了drawable-xhdpi下面的图标,HTC One运行你APP时会自己选择加载那里面的。
2.布局
Android的五大布局分别是LinearLayout(线性布局)、FrameLayout(帧布局)、RelativeLayout(相对布局)、AbsoluteLayout(绝对布局)和TableLayout(表格布局)。其中用得最多的要数LinearLayout和RelativeLayout,FrameLayout和TableLayout可在有特效需求的页面使用,这里就不介绍,而AbsoluteLayout已经被标记位Deprecated since API level 3,不建议再使用。
线性布局的特点是,里面的每个view都是按顺序摆放,要么是垂直要么是水平,谁声明在前面谁显示时就排在前面。而相对布局位置不受排放顺序的限制,可以在xml中指定它位于哪个view的上方、下方、左边或右边。
做自适应最关键的地方就在布局上面,首先拿到需求后分析页面采用何种布局结构才能更好的做到自适应,或者APP设计时就设计得比较简单,而手机端软件的设计规则也是越简单越好用!在布局上尽量使用match_parent个wrap_content,绝对不要使用px(当然如果你的APK只需要支持某一种分辨率除外),非要设置具体大小时使用dp做单位,这样才能在不同DPI但物理尺寸大小一样的手机上面通用。但都用dp并不能满足同时适应手机和平板(物理尺寸存在较大差异),因为平板的物理尺寸比较大通常是7寸以上,举个例子,两个Button将其大小设置为width 50dp height 50dp,它在800*480 尺寸为4寸的手机上面显示效果如下:
而在1024*600的七寸平板上面显示效果如下:
很显然在平板上面显示效果感觉太小了,为什么会出现如此差异,我们可以通过计算得出答案。800*400 4寸 DPI是230,normal状态下DPI是160,据此我们可以算出50dp在这个800*480 4寸设备上面占用的像素是230/160*50 = 72px ,显然这个宽度只占到整个屏幕宽度的72/480=0.15,再看看截图,应该差不多是这么个比例吧。如果是在1024*600的7寸(DPI是170)设备上面50dp占用的像素是170/160*50=53px,这样整个button占据屏幕的宽度就只有53/600=0.09,也就是如截图看到的一样占用很小的一坨。
所以,通过dp作为单位不能解决不同尺寸设备的自适应问题,同样android也提供了相应的解决办法,在android3.2 (API 13)以前对屏幕大小做了如下分类:
要想在布局文件中区分不同尺寸设备可以通过layout-small、layout-normal、layout-large和layout-xlarge,根据我们刚才计算的值,只需在layout-large中指定button的大小为x这个x的值可以通过公式算出来x*(170/160)/600=0.15 x = 600*0.15/(170/160)=95dp,再看下效果如下:
这样就它们显示起来的效果就几乎一样了。同时,不光是大小,显示的位置都可以根据屏幕的大小做不同的显示。一般情况下你需要维护layout layout-large和layout-xlarge三套布局文件,在android 3.2以后官方又提供了更加精确的尺寸大小区分办法,sw<N>dp,其中sw指的是长和宽中的较小者,比如1024*600的7寸,sw的计算方式是600/(170/160)=565dp,但实际是不对的,参考网页《官方六》中的描述:
Note: The sizes that you specify using thesequalifiers are not the actual screen sizes. Rather, the sizes arefor the width or height in dp units that are available to youractivity‘s window. The Android system might use some of the screen forsystem UI (such as the system bar at the bottom of the screen or the status barat the top), so some of the screen might not be available for your layout.Thus, the sizes you declare should be specifically about the sizes needed byyour activity—the system accounts for any space used by system UI whendeclaring how much space it provides for your layout. Also beware thatthe Action Bar isconsidered a part of your application‘s window space, although your layout doesnot declare it, so it reduces the space available for your layout and you mustaccount for it in your design.
所以说需要注意的是计算sw时会有一定得区间浮动,我们需要尽量定义小些,比如565dp如果你定义为sw565dp,但实际是sw564dp,那么就不会走到sw565dp里面去了,而是采用向下找最近的原则,如果都没找到就使用默认的layout。
对于官方为什么会引入sw这个概念,是因为large xlarge已经不能适应各种厂商太多的中间尺寸,比如5.5 、5.7、 6 、 6.5等等,如果是android3.2以下,这些将全部被归入large的范畴,这样的话5.5和7寸设备显示的效果就会有些出入,当然不会有很大出入。为了满足人们对设计的精益求精,在android3.2以后的设备中都可以使用sw来做更细的划分,如sw480dp sw600dp sw720dw sw1024dp等等。
当然为了避免维护如此多的layout布局文件我们还可以用另外的版本来完成自适应的动作。如上面的例子,每个button都是占屏幕宽度的0.15,我们可以用layout_weight权重这一概念,设置该button的权重为0.15即可,它就会在每个设备上面达到相同的效果—均占屏幕宽度的0.15,高也是同理。对与宽高比例一致的设备这是一个不错的选择,但对与不同宽高比的设备,完全使用layout_weight来做自适应显然是不能达到效果的,比如800*480和854*480的设备,如果高采用同样的weight,854的高肯定会显得拉伸。如果button上面有背景图片,则图片会变形。有一个小技巧可以解决此问题,就是用ImageView来代替Button,同时将背景图片设置为android:src,以前景的模式显示,这样可以利用imageview的属性,使图片保持原始的比例填充。
3.数值
布局文件中会用到各种数值,如view的宽、高,字体的大小padding大小,magin大小等均可以在values文件夹中定义其中就包括strings.xml dimens.xml styles.xmlcolor.xml等等,当然跟自适应相关的就是dimens.xml。这里面定义了各种view属性的大小,例如button的高为50dp可以在dimens里面定义<dimen name="btn_size">50dp</dimen>然后将button的android:layout_width="@dimen/btn_size "。在布局部分我们讲到用layout-large layout-sw600dp等来区分不同屏幕大小的设备,同样在values里面也可以加上values-largevalues-sw600dp等来区分,比如最开始的例子里面我们可以只定义一个layout 将button的宽高都设置为@dimen/ btn_size,然后定义一个values/dimen.xml 和一个values-large/dimen.xml,分别写上<dimen name="btn_size">50dp</dimen>和<dimen name="btn_size">95dp</dimen>,这和写多个layout效果是一样的。但我个人觉得如果页面相对比较简单,在values里面下功夫比写多个layout要简单些。
3. 扩展
有时我们APP需要支持到比较旧的API版本,但我们有想用到一些比较高级的API里面的功能,这时我们可以在values后面加上-v<xx>如values-v11,其中11就是API版本,也就是11以上API的设备就会采用values-v11里面的内容。就比如API 11里面加入了Action Bar的功能,那么如果你想利用这个特性,可以在values-v11里面定义一个style.xml,继承自action bar相关的style,而在values里面的style.xml中定义不支持action bar的style,但在布局中用其他view来模拟action bar的效果。
4.总结
对与UI自适应总结一下主要是两点,相同屏幕尺寸不同分辨率以及不同屏幕尺寸相同分辨率。对于前者,只需在布局中用到单位的地方全部用dp即可解决(有条件的话drawable最好能提供不同分辨率icon,实在不行,提供允许范围内尽量大分辨率图片)。对于后者,则需要根据尺寸的等级写不同的layout文件或者values文件,这个需根据项目的具体负责程度来自由选择。