要实现程序的多屏适配,需要完成两个任务,一个是使得UI在不同的dpi下显示效果一致,即屏幕像素密度无关;另一个是为不同的屏幕尺寸设计合适的UI布局。
屏幕像素密度无关
在android屏幕分类中我们提到过,android设备的屏幕有不同的dpi(dots per inch,即像素密度),对160dpi的屏幕,1英寸有160个像素点,而对于240dpi的屏幕,1英寸则有240个像素点,也就是说,dpi越高,同样大小的区域内包含越多像素点。这就会造成同样像素大小的控件,在不同dpi的屏幕上显示的大小会不一样,如下图所示:
上图是两个screen size相等的屏幕,左边屏幕的分辨率为470 x 320,属于mdpi,右边屏幕分辨率为940 x 320,属于xhdpi,绿色方块是一个像素大小为300 x 300的ui控件。因为dpi越高,每英寸所包含的像素数越多,像素点越小,所以对于同样的控件,在dpi高的屏幕上显示会比较小。
像素无关单位DP
针对上面的问题,android提供了一种像素无关的单位dp(又叫dip,Density-independent pixel)
dp与px的换算公式为:px = dp * (dpi / 160)
系统会根据屏幕的dpi,将dp单位转化成适当的px值,由公式可知,对于160dpi(mdpi)的屏幕,1dp = 1px,对于240dpi(hdpi)的屏幕,1dp = 1.5px。只要用dp为单位定义UI控件的大小和位置,就可以使UI在不同dpi的屏幕上实现一致的显示效果,从而实现屏幕像素密度无关。例如上面的例子,只要在layout布局文件中把控件的宽高都定义成300dp,那么系统将会根据屏幕的dpi将该控件转化成适当的像素大小,如在mdpi下为300 x 300像素,在xhdpi下为600 x 600像素,如下:
常见的屏幕dpi和大小关系如下:
这5中主要的屏幕密度满足2:3:4:6:8的比例关系。
可以看到,dp单位是与屏幕密度无关的,不管你的屏幕dpi是多少,只要再代码中以dp为单位定义UI控件,那么在不同的屏幕密度下,视觉大小就能确保一致。
android系统以两种方式来帮助应用程序实现屏幕密度无关
- 系统根据当前屏幕dpi将dp单位转换成适当的px值
- 系统根据当前屏幕dpi将drawable资源缩放成适当的大小
第一种情况前面已经讲解了,我们来看下第二种情况。android使用到的图片(bitmap)放在drawable资源文件中。在android工程中,会根据不同的dpi创建相应的drawable目录,如下:
系统首先根据当前屏幕的dpi加载相应drawable目录的图片,如果没有找到,系统会用最优的方法去其他目录寻找,直到找到为止。比如,工程中只在drawable-mdpi目录下放了图片,当在hdpi屏幕的设备上运行这个程序时,系统会从drawable-mdpi目录下加载图片,并将其放大(按照dpi的比例关系缩放),使其在屏幕中可以按照适当的尺寸显示。
然而位图的缩放会照成图片的模糊或者产生图片像素化,为了提供良好的用户体验,我们需要提供标准屏幕dpi的图片。而不是只提供一种图片,然后通过系统的机制对其进行缩放。例如,如果在mdpi有一张100 x 100像素的图,那么对应的需要
hdpi:150 x 150
xhdpi:200 x 200
xxhdpi:300 x 300
如下图所示:
这样,系统在就能加载到对应屏幕dpi分类的图片文件,然后在适当的范围内进行缩放,保证图片不会产生严重的失真。
为不同的屏幕尺寸提供合适的布局
讲完screen densities(屏幕像素密度)对多屏适配的影响,接下来我们看screen sizes(屏幕尺寸)是如何影响多屏适配的。首先看下图:
通过上图可以看到,虽然已经实现了屏幕像素密度无关,即同一控件在不同的屏幕密度(dpi)下,显示的大小是一样的。显然上面两张图ui控件的大小是一样的,但因为大的屏幕尺寸意味着更多的布局空间,适用于小屏的UI的布局和大小,在大屏幕上,可能无法充分利用屏幕空间,造成屏幕显示不饱满,留下大片的空白,影响了用户体验。
所以为了使得程序在不同的screen sizes(屏幕尺寸)下都有很好的用户体验,在必要时需要为大尺寸的屏幕重新设计UI布局或者UI的大小。如果我们为大屏幕重新定义了UI图片的大小,如前所述,为了实现屏幕像素密度无关,那么我们就需要为该图提供mdpi,hdpi,xhdpi,xxhdpi...的大小的版本。
当我们在android工程中为不同的屏幕尺寸提供了不同的UI布局,那么程序在不同的尺寸运行时怎么知道加载那个layout布局文件了,android系统会自动完成这些工作,只要我们正确的定义了布局文件的目录名字,类似与图片文件的加载。
在 android屏幕分类中有提到,android3.2以前,屏幕尺寸(screen sizes)是以small,normal,large,xlarge来进行分类的,到时这样的分类并不够精细,如5寸的手机和7寸的平板同被分到了large类,显然这两者的屏幕差别是很大的。所以在android3.2以后,对屏幕尺寸的分类做了修改,现在常用的一种分类方式是swdp。如下所示,
swdp表示屏幕的较窄的一边,与屏幕的方向无关。
例如一般的
7
寸平板的最小宽度是
600dp
,那么可以使用
sw600dp
修饰符去修饰资源文件,这样当屏幕的宽度大于
600dp
时,系统会加载使用该修饰符的资源文件。
当设备最小屏幕宽度达到600dp时,使用左右双屏显示的布局。
使用orientation修饰符
很多情况下,特别是pad,不同方向下使用的布局时不一样的,而方向的改变也会影响屏幕当前宽度和高度,因此在资源文件中经常会使用到orientation修饰符
常用的资源修饰符组合
dpi修饰符,size修饰符和orientation修饰符可以组合使用
在代码中将dp转换成px:
总结:
1. 使用dp,wrap_content,match_parent来定义UI控件,text大小用sp,不要使用px。
2. 在mdpi,hdpi,xhdpi,xxhdpi的drawable目录中放置对应大小的位图,系统会根据屏幕dpi加载相应目录的图片。
3. 为不同的屏幕大小定义相应的资源文件,使用screen size修饰符和orientation修饰符,如layout-sw600dp-port。