由于Android系统的开放性,因此任何用户、开发者、OEM厂商、运营商都可以对它进行定制,修改成他们想要的样子。
下面这张图是Android屏幕尺寸的示意图,在此图中蓝色矩形的大小代表不同尺寸,颜色深浅代表所占百分比的大小。
从上图可以看出Android的屏幕尺寸特别多,为了让我们开发的应用程序能够比较美观的显示在不同尺寸、分辨率、像素密度的设备上,那就需要在开发过程中进行相应处理。
详细的统计数据请到这里查看。
不同分辨率密度之间的关系:
Android中的mdpi、hdpi、xdpi、xxdpi、xxxdpi用来修饰drawable及values文件夹,区分不同像素密度下的图片和dimen值。
Google官方指定按照下列标准进行区分:
开发过程中需要将合适大小的图片放在合适的文件夹下面。
设计图标时需对五种主流的像素密度(mdpi、hdpi、xdpi、xxdpi、xxxdpi)按照2:3:4:6:8的比例进行缩放。比如一个启动图标的尺寸为48x48dp,这表示在mdpi的屏幕上其实际尺寸应为48x48px,在hdpi的屏幕上其实际大小是mdpi的1.5倍即为72x72px,在xdpi的屏幕上其实际大小是mdpi的2倍即为96x96px依此类推。虽然Android平台也支持低像素密度ldpi的屏幕,但随着移动设备配置的不断升级此像素密度的设备已经很少见了,而且对于此类屏幕的手机系统会自动将mdpi尺寸的图标缩小进行匹配。
下图是图标在各个屏幕密度下的对应尺寸大小:
一、布局自适应屏幕
使用wrap_content、match_parent、weight属性来设置某些视图组件的宽度和高度。
使用“wrap_content”系统就会将相应视图的宽度或高度设置成刚好能够包含视图中内容的最小尺寸,而使用“match_parent”(低于Android API 8的级别中为“fill_parent”)则会让视图的宽和高延伸至充满整个父布局,weight是线性布局的一个独特属性,使用它可以按照比例对界面进行权重的分配,完成一些特殊的需求。
使用RelativeLayout相对布局。
开发过程中各种布局的特点不一样,我们应该选择不同的布局以完成最优化的布局需求。对于一些相对位置固定或者不是一条直线排列的布局来说我们可以使用RelativeLayout相对布局
二、根据屏幕配置来加载合适的UI布局
使用尺寸(Size)限定符
现在有很多大屏的设备,我们的应用程序为了更好的适配这些屏幕提升用户体验,往往会根据屏幕的大小来实现不同的布局,为了适配不同的屏幕我们应该杂么做呢?Android中提供了限定符来满足我们这种情况的设定。
在第二个布局的目录名中包含了large限定符,系统会在较大屏幕的设备(比如7寸以上的平板)上加载此布局,而在小屏设备上会加载另一个默认的布局。
使用最小宽度(Smallest-width)限定符
在版本低于3.2的Android设备上使用Size限定符会有一个问题,就是其中的“large”到底是指屏幕多大的尺寸范围,该问题会影响戴尔Streak、早期的Galaxy Tab以及大部分7英寸平板电脑,即使这些设备的屏幕属于“较大”的尺寸,但是很多应用可能会针对此类别中的各种设备(如5英寸和7英寸的设备)显示不同的布局,这就是Android在3.2版本之后引入“Smallest-width”限定符的原因。
“Smallest-width”限定符允许通过设定一个具体的最小宽度(以dp为单位)值来指定屏幕。例如标准7英寸平板电脑的最小宽度为600dp,因此如果你想要在此类屏幕上的用户界面中使用双面板(但在较小的屏幕上时只显示单面板),可以使用sw600dp来表示你想在最小宽度为600dp以上的屏幕中使用双面板布局,而不是使用large尺寸限定符。
也就是说,对于最小屏幕宽度大于等于600dp的设备系统会选择layout-sw600dp/main.xml(双面板)布局,而更小屏幕的设备将会选择layout/main.xml(单面板)布局。
然而在低于Android 3.2版本的设备上时将无法识别sw600dp这个尺寸限定符,因此我们还仍需使用large限定符。这样就需要在res/layout-large和res/layout-sw600dp目录下都添加一个相同的main.xml。
使用布局别名
最小宽度(Smallest-width)限定符仅适用于Android 3.2及更高版本,因而需要同时使用Size限定符(small、normal、large和xlarge)来兼容更早的系统。例如你想在小屏手机上显示单面板,在7英寸平板或者其他较大屏幕的设备上显示多面板时,我们就需要提供以下文件:
最后两个文件是完全相同的,为了避免此类布局文件出现重复,你可以使用别名来定义文件。例如可以使用如下方式来定义布局:
然后添加以下两个文件:
<resources>
<item name="main" type="layout">@layout/main_twopanesitem>
resources>
<resources>
<item name="main" type="layout">@layout/main_twopanesitem>
resources>
这两个文件的内容相同,但它们并未实际定义布局。它们仅仅是给main定义了一个别名main_twopanes,这样两个layout.xml都只是引用了@layout/main_twopanes,避免了重复定义布局文件的情况。由于文件包含large和sw600dp限定符,因此无论Android版本如何系统都会将这些文件应用到不同屏幕的设备上(版本低于3.2时会匹配large文件,版本高于3.2时会匹配sw600dp文件)。
使用屏幕方向(Orientation)限定符
某些布局会同时支持横向模式和纵向模式,但在多数情况下我们还可以通过调整优化其中大部分布局的效果。在新闻阅读器示例应用中,不同屏幕尺寸和不同屏幕方向下的布局方式如下所示:
所有这些布局都定义在res/layout/目录下的某个XML文件中,为了让不同的设备根据屏幕配置来加载正确的布局,程序需要使用布局别名来实现。
上面我们已经定义了所有可能的布局,接下来只需要使用限定符来让不同的设备根据屏幕配置加载正确的布局即可。我们使用布局别名来实现:
<resources>
<item name="main_layout" type="layout">@layout/onepane_with_baritem>
<bool name="has_two_panes">falsebool>
resources>
<resources>
<item name="main_layout" type="layout">@layout/twopanesitem>
<bool name="has_two_panes">truebool>
resources>
<resources>
<item name="main_layout" type="layout">@layout/onepaneitem>
<bool name="has_two_panes">falsebool>
resources>
<resources>
<item name="main_layout" type="layout">@layout/twopanesitem>
<bool name="has_two_panes">truebool>
resources>
<resources>
<item name="main_layout" type="layout">@layout/twopanes_narrowitem>
<bool name="has_two_panes">truebool>
resources>
三、根据屏幕大小自动伸缩图片
使用自动拉伸(Nine-Patch)图片
支持不同屏幕尺寸意味着所使用的图片资源须能自适应各种尺寸。例如一个按钮的背景图片无论应用到什么形状的按钮上都能够随着按钮的大小改变而改变。
如果使用普通的图片实现上述需求你会发现显示出来的效果在不同的设备上差别很大,因为系统在运行时会均匀地拉伸或压缩图片。Android中提供了一种特殊格式的PNG图片,它可以指定可以拉伸以及不可以拉伸的区域。
Android SDK中带有.9图片的制作工具draw9patch(工具位置在SDK的tools目录下),.9图片的制作实际上就是在原图矢量图上添加1px的边界,然后按照我们的需求把对应拉伸的位置设置成黑色线,系统就会根据我们的实际需求进行拉伸。
使用时需要注意的是这张图片的后缀名是.9.png,系统会根据这个来区别Nine-Patch图片和普通的PNG图片的。
下图是对.9图的四边含义解释,左上边代表拉伸区域,右下边代表padding box即间隔区域。
我们通过下面两张图来理解一下这四条线的含义:
上图和下图的区别在于右下边的黑线不一样,具体效果展示的区别可以看右边的效果图。上图效果图中深蓝色的区域代表的是内容显示区域,可以看到是在正中间的,这是因为在右下边的是两个点,这两个点距离上下左右四个方向的距离就是padding的距离,所以深蓝色内容区域在图片正中央。而在下图中,由于右下边的黑线是图片长度,所以就没有padding距离,从效果图上的显示就是深蓝色区域和图片一样大小,因此我们可以利用右下边的点的长度和位置来控制内容与背景图片边缘的padding距离。
下面看下设置左上边时的效果,我们只设置了左上边线,效果图如下:
上面的线没有包住图标而下面的线正好包住了图标,从右边的显示效果图应该可以看出两个的差别,黑线所在的区域就是拉伸的区域。上图黑线所包含区域全是纯色,所以图标没有变形;而下图的拉伸区域包裹了图标,所以图标在拉伸的时候就会对图标进行拉伸,因此就导致了图标的变形。注意到下图中的红线区域了吗?这是系统提示我们的,因为这样拉伸不符合要求,因此系统进行了提示。
四、使用非密度制约的像素
由于各种屏幕的像素密度都有所不同,因此相同数量的像素在不同设备上的实际大小也有所差异,这样使用像素定义布局尺寸时就会产生问题。因此须使用dp或sp单位来指定尺寸。dp是一种非密度制约像素,其尺寸与160dpi像素的实际尺寸相同。sp也是一种基本单位,但它可以根据用户的偏好文字大小进行相应调整,因此我们应将该测量单位用于定义文字的大小。
五、提供备用位图
由于Android应用程序可在不同屏幕密度的设备上运行,因此我们提供的位图资源应始终可以满足各类密度范围要求(低密度、中等密度、高密度以及超高密度),这有助于我们的图片资源在不同屏幕密度上都能展示出色的质量和效果。要生成不同密度下的图片,我们须先提取矢量图的原始资源,然后根据以下尺寸范围针对各密度生成相应的图片大小。
也就是说,如果我们为xxhdpi设备生成了300x300px尺寸的图片,那就应该使用同一资源图片为xhdpi、hdpi、mdpi和ldpi设备分别生成200x200、150x150、100x100和75x75尺寸的图片。然后将生成的图片文件放在res下相应的子目录中(ldpi、mdpi、hdpi、xhdpi、xxhdpi),系统会根据运行应用设备的屏幕密度自动来选择合适的图片进行加载。这样我们只要使用@drawable/id来引用图片资源,系统就可以根据相应屏幕的dpi来选取合适的位图加载。
这里需要注意的是如果为.9图或者是不需要适配不同分辨率的图片就放在drawable文件夹即可,对应分辨率的图片要正确的放在合适的文件夹下,否则会造成图片拉伸等问题。
另有一种百分比适配方案可参考鸿洋的文章: Android屏幕适配方案