本文主要是介绍Anddroid 适配多种屏幕,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Android 运行在各种不同尺寸和屏幕密度的设备上。对于应用来说,Android系统提供了一个稳定的开发环境,然后在
调整应用界面显示的问题上也花了很多的功夫。与此同时,为了能够在不同配置的屏幕上去优化你的UI设计,Android
系统也提供相应的APIs以允许你对于特定尺寸和密度的屏幕进行控制。比如说,你想要你的UI去适配平板,而不是手机。
尽管Android系统可以对你的应用进行缩放以重新调整大小去适应不同的屏幕,但是,针对不同尺寸和密度的屏幕,你还需要花一定的时间去优化。这样做,对于所有设备而言,可以最大限度地提升用户的体验,以至于让用户感觉你的应用实际上正是针对他的这种设备而进行开发的,而不是通过拉伸去适配他们的设备。
通过下面这些练习,你可以让你的应用正常显示,还能在所想要支持的屏幕上去最大化用户体验。
注意:这个文档的内容是针对Android 1.6 (API Levle 4)或者更高版本的应用而言。如果你的应用需要去支持 Android 1.5或者更低版本,请先阅读Strategies for Android 1.5.
还需要知道一点的就是:Android 3.2 引进了新的APIs,可以让你更精准地去控制你的布局文件以适配不同尺寸的屏幕。如果你想要开发一款应用去适配平板的话,那么,这些新的特性就显得更为重要了。更多详细信息,可以参考这个部分 Declaring Tablet Layouts for Android 3.2
屏幕适配概述
这个部分主要是关于Android 适配多种屏幕的一个概述,包括:术语和概念的介绍,屏幕配置总结,API概述以及和潜在的屏幕兼容特性。
术语和概念
屏幕尺寸
实际的物理尺寸,其是通过测量屏幕对角线而来。为简单起见,Android把所有的屏幕尺寸分成了四类:
small, normal, large,和 extra-large
屏幕密度
单位屏幕区域内像素的数量;通过称为 dpi (每英寸所含的像素点数)。举个例子,在屏幕尺寸一定的情况下,低密度
屏幕所含有像素的数量比“normal”或者“high”密度的屏幕所含的像素数量要少。为简单起见,Android把所有的屏幕密度分成了六类:low, medium, high, extra-high, extra-extra-high, and extra-extra-extra-high
方向
屏幕的方向是从用户的角度去做区分的。它要么横屏,要么竖屏,这意味着屏幕的纵横比要么是宽的,要么是高的。需要知道的是,不仅在不同的屏幕方向上可以做不同的设备操作,并且当用户旋转屏幕的时候,方向还可以在运行时发生变化。
分辨率
就是一块屏幕所包含的所有像素点数的总和。当对多种屏幕进行适配的时候,应用没必要去考虑分辨率的问题。应用只需要关心屏幕尺寸和屏幕密度就可以。因为分辨率已根据屏幕尺寸和屏幕密度去进行了一个划分。
密度无关像素(dp)
dp是一个虚拟的像素单位,在定义UI布局的时候,你可以以 dp 的方式去使用它,以说明布局的尺寸或者位置。系统是以160 dpi的屏幕密度作为基准的。在屏幕密度为160 dpi的屏幕上,一个dp等于一个像素。在运行期间,系统会以
实际的屏幕密度去计算以dp为单位的那些尺寸。dp单位转化为屏幕像素是非常简单:px = dp * (dpi/160) .举个例子,在240 dpi的屏幕上,1dp 等于1.5个像素。当定义你应用的UI的时候,你应该去使用dp单位以确保在不同密度的屏幕都能够很好地去显示你的UI。
屏幕适配的范围
从Android 1.6 (API Level 4)开始,Android提供了对多种屏幕尺寸和密度进行适配的解决方案,反映出一个设备可能需要多种不同的屏幕配置。你可以针对每种屏幕配置,使用这些特性去优化你应用的用户界面,以确保你的应用不仅可以正常显示,还可以在每个屏幕上,给用户一个最好的体验效果;
为了简化你的用户界面对多种屏幕适配的问题,Android将实际的屏幕尺寸和屏幕密度划分为:
四类屏幕尺寸:small, normal, large, and xlarge
须知:
从Android 3.2 (API Level 13)开始,这种尺寸分组就不被推荐使用了,因为它被一种新的管理屏幕尺寸的技术所取代了。如果你是在Android 3.2或者更高版本上进行开发,关于其更多的信息,你可以参考 Declaring Tablet Layouts for Android 3.2
六类屏幕密度:
ldpi (low) ~120dpi
mdpi (medium) ~160dpi
hdpi (high) ~240dpi
xhdpi (extra-high) ~320dpi
xxhdpi (extra-extra-high) ~480dpi
xxxhdpi (extra-extra-extra-high) ~640dpi
这几类屏幕尺寸和密度的划分是以 normal 尺寸和 mdpi的密度为基准线进行的。而这个基准线又是依据第一代Android设备 T-Mobile G1的屏幕配置进行的。
每一类尺寸或密度,都表示着某一个范围内的实际屏幕尺寸和屏幕密度。举个例子,两个都是 normal类的屏幕尺寸的设备,它们可能会有一个轻微不同的实际屏幕尺寸和纵横比。同理,两种都是 hdpi 的设备,但它们实际的像素密度可能会有轻微的不同。在应用层面上,Android将这些不同进行了一个抽象,所以你可以只为这几大类屏幕尺寸和屏幕密度进行UI的设计。
然后如果有必要的话,让系统去进行一个最后的调整。图1 说明了不同尺寸和密度是如何粗略地划分为不同的尺寸类和密度类的。
图1
由于你需要为不同的屏幕尺寸去设计UI,接下来你会发现每一种设计都需要一个最小的空间去作为限定。所以每一种
屏幕尺寸的分类就和系统所定义的最小分辨率联系在了一起。这些最小尺寸都是以 “dp” 为单位,在你定义你的布局的时候,你也应该去使用相同的这种单位,因为这样系统就不用关心关于屏幕密度变化的问题。
xlarge screens are at least 960dp x 720dp
large screens are at least 640dp x 480dp
normal screens are at least 470dp x 320dp
small screens are at least 426dp x 320dp
须知:在Android 3.0之前,这些最小屏幕尺寸并没有很好地去进行定义,所以你可能会遇到一些设备,它们的分类是介于 normal 和 large之间的。它们也是基于屏幕的分辨率,所以在设备上有可能会有些不同。比如:一个分辨率为 1024 * 720并带有系统栏平板,对于应用来说,它实际上只有更少的空间可以使用,因为部分空间已经被系统栏所占用。
为了对不同屏幕尺寸和屏幕密度去优化你应用的UI,你可以对每种类别的尺寸和密度提供一个alternative resources。 通常,针对某些不同尺寸的屏幕,你应该提供一个可供选择的布局,然后针对不同密度的屏幕,你应该提供可供选择的位图图像。在运行的时候,系统会根据当前设备屏幕的尺寸类别和密度类别去为你的应用选择合适的资源。
你不需要为每一种屏幕尺寸和屏幕密度的组合去提供一个可供选择的资源。系统提供一个强大的兼容特性完成了将你的应用渲染到屏幕上的大部分的工作。
须知:设备的屏幕尺寸类别和屏幕密度类别两者是相互独立开来的。例如:一个 WVGA的高密度的屏幕,它却是一个normal尺寸的屏幕,因为它的物理尺寸是和 T-Mobile G1一样的。另一方面,一个 WVGA的 中等密度的屏幕却是一个 large尺寸的屏幕。然而这两者分辨率相同( 像素总和一样 ) ,一个 WVGA 的中等密度的屏幕有着较低的屏幕密度,意味着每一个像素所占的物理尺寸就较大一些,因此,整个屏幕就比 normal 尺寸的屏幕大。
密度无关
当在不同屏幕密度上进行显示时,你的应用保留了用户界面元素的实际物理尺寸,而去实现了屏幕密度无关的一个效果。保持屏幕密度无关是非常重要的,因为,没有它,一个 UI 控件(比如 button)在低密度的屏幕上就会显得非常大,在高密度的屏幕上就会显得比较小;这些屏幕密度相关尺寸的改变在应用的布局中会引发一些问题,图2和图3展示了在应用中没有使用屏幕密度无关和使用屏幕密度无关时的区别。
图2 (屏幕密度相关)
图3 (屏幕密度无关)
Android 系统有两种方式去帮助你的应用实现屏幕密度无关:
1>
系统会根据当前屏幕密度,将以dp为单位的元素缩放至合适大小
2>
系统会根据当前屏幕密度,将drawable资源文件缩放至合适大小。
在图2中,文本和图片的尺寸是以px为单位的,所以这些控件在低密度上就显得比较大,而在高密度的屏幕上就显得比较小。尽管实际屏幕尺寸也许一样,但是高密度的屏幕每一英寸却拥有着更多的像素。在图3中,布局的尺寸被指定为以 dp 为单位。由于屏幕密度无关的基准是以中等屏幕密度作为基准的,所以这个中等密度的设备显示的效果和图2一样。而对于低密度和高密度的屏幕,系统对屏幕密度无关像素值进行了一个缩小和放大以适应屏幕。
在大多数情况下,在你的应用中,你可以仅仅只使用屏幕密度无关像素或者使用 “wrap-content”作为你布局文件尺寸的单位以确保能够显示至合适位置。然后系统也会根据一个合适的缩放因子将位图图片缩放至合适尺寸。
然而,位图缩放会导致位图模糊化或像素化,也许你在上边的截图中已经注意到了这一点。为了避免这些问题,你应该针对不同屏幕密度去提供一个可供选择的位图资源。
如何适配多种屏幕
在当前屏幕配置中,将应用的布局和位图图片以一种恰当的方式去进行渲染的这种能力是多屏幕适配的一个基础。针对每一种屏幕配置,在将应用去进行一个恰当的渲染的时候,系统已做了大部分的工作量。但为了能够更完美地处理每种不同屏幕配置,你还需要:
在清单文件中显示声明你的应用需要适配什么尺寸的屏幕
<supports-screens> 节点包含到清单文件中。
对于不同屏幕尺寸提供不同的布局
默认情况下,Android 会重新调整你应用布局的大小以适应当前设备屏幕。在大多数情况下,这是没问题的。但在某些情况下,你的 UI 可能看起来不是那么好看,可能需要对不同屏幕尺寸去做一个调整。举个例子,在一个较大的屏幕上,你也许想要利用空余的屏幕空间去调整一些控件的位置和尺寸,在一个较小的屏幕上,你可能需要去调整尺寸以便让所有的控件都能显示出来。
你可以使用 small , normal, large, xlarge 配置限定符去修饰特定尺寸的资源。举个例子,对于一个extra-large屏幕的布局文件,它应该放入到 layout-xlarge/目录下。在 Android 3.2 (API level 13)之后,上述的这个尺寸分类就不被推荐使用了。取而代之的是:你可以使用 sw<N>dp配置限定符为你的布局资源文件去定义一个最小可用的宽度。举个例子,如果你的多窗格平板电脑的布局要求最小在 600dp 以上的屏幕宽度,那么你就应该将这个布局文件放到 layout-sw600dp/ 目录下。这种新的声明布局资源文件的技术在 Declaring Tablet Layouts for Android 3.2 这里有讲解到。
为不同屏幕密度提供不同的位图图片
默认情况下,Android 会针对每种设备,将你的位图图片(.png
, .jpg
,和 .gif
图片
)以及 Nine-Patch 图片 (.9.png图片)缩放至合适的尺寸。举个例子,如果你的应用仅仅只为 中等屏幕密度提供位图,当在高密度的屏幕中,系统会放大这些图片,当在低屏幕密度的时候,系统会缩小这些图片。这种缩放会使位图图片出现一些问题。为了确保你的位图可以看起来效果最佳,你应该针对不同屏幕密度,让其包含不同的分辨率版本的图片。
针对特定密度的资源,你可以使用ldpi
(low), mdpi
(medium), hdpi
(high), xhdpi
extra-high), xxhdpi
(extra-extra-high), and xxxhdpi
(extra-extra-extra-high)限定符去修饰。例如,针对高密度屏幕的位图,你可以将其放到drawable-hdpi/目录下。
须知:
只有在需要去提供一个启动图标的时候,
修饰符才会用得到。你没必要为应用所有的图片都去提供一个mipmap-xxxhdpi
xxxhdpi 的资源。
有些设备将启动图标进行 0.25 倍放大的操作。例如,如果说你的最高密度的启动图标已经是 xxxhdpi 了,然后这个放大会使图片看起来不是那么清晰,那么此时你应该在mipmap-xxxhdpi目录下去提供一个更高密度的启动图标,系统会使用这个mipmap-xxxhdpi目录的图标,而不是将原来 xxxhdpi 的图片去进行一个放大操作。
更多信息,可以参考Provide an xxx-high-density launcher icon 。除了启动图标之外,你不要用 xxxhdpi 对 UI 元素 进行修饰。
须知:
所有的启动图标应该放到 res/mipmap-[density]/目录下,而不是放到res/drawable-[density]/目录下。在你的app
安装的时候,不管你屏幕的分辨率是多少,Android系统都会保留这些指定密度目录下的资源,比如mipmap-xxxhdpi。这种机制就可以允许启动应用程序为你的app去选择一个最佳分辨率的图标展示在home界面上。更多关于使用 mipmap目录的信息,可以参考Managing Projects Overview.
这些尺寸和密度配置修饰符与上述所提及的尺寸和密度类别是一一对应的。
须知:
如果你不熟悉配置修饰符以及不熟悉系统是如何运用可供选择的资源的,你可以参考Providing Alternative Resources
在运行期间,对于给定的资源,系统会通过如下步骤以确保在当前屏幕上能够以最佳效果去把它们显示出来:
1>系统使用合适的可供选择的资源
根据当前屏幕的尺寸和密度,系统会使用应用中对应尺寸和密度的资源。举个例子,如果这个设备的屏幕是属于高密度的屏幕,然后应用又需要一个图片资源,然后系统会到与设备配置最匹配的图片资源目录下去查找,hdpi修饰符的资源目录(如drawable-hdpi/目录 )是一个最佳的匹配,所以系统就会去使用这个目录下的图片资源。
2>如果没有匹配的资源可用,系统会使用默认的资源,然后将其放大或缩小以适应当前屏幕尺寸和密度这个“默认”资源是指没有用配置修饰符修饰的那些资源。举个例子,drawable/目录下的资源就是一个默认的图片资源。系统
会认定这些默认的图片资源都是 normal尺寸和中等密度的。所以,对于高密度的屏幕,系统会将这些默认的资源予以放大。对于低密度的屏幕,系统会将这些默认的资源予以缩小以适应当前屏幕。但是,当系统去查找指定的资源,在指定密度的目录下却没能找到时,它就不一定总是会去使用这个默认的资源。系统可能会使用其中的一个指定密度的资源去取而代之,以确保缩放到一个较好的效果。举个例子,系统在查找低密度的资源,然后这个资源不可用,系统则侧重将高密度的资源予以缩小来实现,因为相比于通过0.75倍缩放因子从中等密度缩小到低密度而言,通过0.5倍
缩放因子,高密度缩小到低密度其有着较小的失真效果。
更多关于Android如何通过匹配配置限定符去选择可供选择资源的信息,可以参考How Android Finds the Best-matching Resource
使用配置限定符
Android系统提供了几种配置限定符,让系统可以根据当前设备屏幕的特性去加载对应可供选择的资源。配置限定符,它就是一个字符串,你可以将配置限定符这个字符串添加到你工程的资源目录上, 用以说明这个资源是针对你所指定的这个配置的。
1>在你工程的 res/ 目录下创建一个新的目录,然后用以下格式去命名这个新建的目录:
<resources_name>-<qualifier>
<resources_name>是一个标准的资源的名称 (比如 drawable 或者 layout)
<qualifier>是一个配置限定符,如以下 表1 所示,用来说明这些资源是为哪种屏幕配置所使用的 (比如 hdpi 或 xlarge)一次可以使用多个 限定符,限定符与限定符之间用破折号隔开
举个例子, xlarge 是一个针对 extra-large 屏幕的配置限定符。当你将这个限定符添加到一个资源目录名上时( 比如 layout-xlarge),它就表示告诉系统这些资源都是用在有 extra-large 屏幕的设备上的。
表1 配置限定符
须知:
如果你开发的应用是用在 Android 3.2 或者更高版本上,那么你可以参考 Declaring Tablet Layouts for Android 3.2 去使用新的配置限定符。在声明布局资源的时候,你可以使用这些新的配置限定符去代替 表1 中的尺寸限定符。
更多关于这些限定符是如何与真实的屏幕尺寸和屏幕密度对应的信息,可以阅读本章上边所提及的屏幕适配范围 一节。
举个例子,下面这些应用资源目录为每种不同屏幕尺寸提供一个不同的布局设计,还提供了不同的drawables。
mipmap文件夹是针对启动图标的。
res/layout/my_layout.xml // layout for normal screen size ("default")
res/layout-large/my_layout.xml // layout for large screen size
res/layout-xlarge/my_layout.xml // layout for extra-large screen size
res/layout-xlarge-land/my_layout.xml // layout for extra-large in landscape orientationres/drawable-mdpi/graphic.png // bitmap for medium-density
res/drawable-hdpi/graphic.png // bitmap for high-density
res/drawable-xhdpi/graphic.png // bitmap for extra-high-density
res/drawable-xxhdpi/graphic.png // bitmap for extra-extra-high-densityres/mipmap-mdpi/my_icon.png // launcher icon for medium-density
res/mipmap-hdpi/my_icon.png // launcher icon for high-density
res/mipmap-xhdpi/my_icon.png // launcher icon for extra-high-density
res/mipmap-xxhdpi/my_icon.png // launcher icon for extra-extra-high-density
res/mipmap-xxxhdpi/my_icon.png // launcher icon for extra-extra-extra-high-density
更多关于如何使用可供选择的资源以及一整套配置限定符的使用,可以参考 Providing Alternative Resources.
需要知道的一点就是:在运行期间,系统去加载资源使用的时候,系统使用了一定的算法去决定什么是最佳匹配的资源。也就是说,你使用的限定符不用很准确地去匹配当前屏幕的配置。尤其是,当系统根据尺寸限定符去选择资源的时候,如果系统没有找到更加匹配的资源,系统会选择比当前屏幕尺寸更小的资源去予以使用(比如,如果必要的话,在一个large-size 的屏幕中,系统会去加载 normal-size 的资源)。更多关于系统如何选择资源文件的信息,可以参考How Android Finds the Best-matching Resource.
提示:
如果你有一些不想让系统去进行缩放的图片资源,那么你就应该放在使用 nodpi 配置限定符进行修饰的目录下。使用这个限定符的资源会被认为是密度无关资源,然后系统就不会去缩放它们。
设计可供选择的布局和图片
这个可供选择的资源应该是根据你应用的需求而定的。通常,你应该使用尺寸和方向限定符去提供一个可供选择的布局资源,使用密度限定符去提供一个可供选择的位图图片资源。
下面这个部分总结了如何使用尺寸限定符去提供一个可供选择的布局以及如何使用密度限定符去提供一个可供选择的图片。
可供选择的布局
通常,一旦你在不同配置的屏幕上去测试你的应用时候,你就会知道你是否需要为不同的屏幕尺寸提供一个可供选择的布局。举个例子:当你在一个small 尺寸屏幕上进行测试时,你也许会发现你的布局并不没有很好地与你的屏幕进行一个匹配。例如,一排按钮也许并没有和小屏幕的宽进行一个很好地匹配。在这种情况下,你需要为小屏幕提供一个可供选择的布局,让其可以去调整按钮的尺寸和摆放的位置。
当在一个 extra-large 屏幕上进行测试时,你也许意识到了你的布局并没有充分地去利用这个大屏,然后可以很明显观察到,它是通过拉伸去适配屏幕的。在这种情况下,你应该为 extra-large 提供一个重新设计的可供选择的布局,让其可以对类似平板这种大屏去做一个优化。
在测试横屏的时候,你也许也注意到了,那些在竖屏中处于底部的 UI 控件 应该让其在横屏时,处于屏幕的右边。
总结,你应该确保你应用的布局:
1>能够适配小屏幕
2>是否针对大屏幕多余的控件也进行了充分的利用
3>是否对横竖屏的情况也进行了优化
如果你 UI 使用了那些需要去适配控件尺寸的位图(比如 一个 按钮的背景图片),那么你就应该使用 Nine-Patch 位图。一个Nine-Patch文件是基于一张PNG图片,但与这张PNG图片不同的是,其在指定区域的平面内是可拉伸的。因此,你不需要为不同屏幕尺寸去提供不同的图片,因为 Nine-Patch 位图可以调整至任何尺寸。所以你需要做的是:针对不同屏幕密度去提供一个可供选择的 Nine-Patch 文件。
可供选择的图片
图4 每种密度图片相对的大小
由于几乎每张图片都需要一张启动图标,而这张图标又得保证能在所有屏幕密度上都能很好地进行展示,所以几乎每个应用都需要针对不同屏幕密度去提供一个可供选择的图片资源。同样,如果在你的应用中包含了其他位图图片(比如 菜单图标或其他图形),你也需要针对不同屏幕密度去提供一份可供选择的位图图片。
须知:
为了对不同密度去创建一个可供选择的位图图片,你需要在六类屏幕密度中去遵循 3:4:6:8:12:16这样一个缩放比例。例如,你有一张 48*48 像素的中等密度的位图图片,那么,在所有不同尺寸中,它们应该是:
- 36x36 (0.75x) for low-density
- 48x48 (1.0x baseline) for medium-density
- 72x72 (1.5x) for high-density
- 96x96 (2.0x) for extra-high-density
- 144x144 (3.0x) for extra-extra-high-density
- 192x192 (4.0x) for extra-extra-extra-high-density (launcher icon only; see note above)
更多关于图标设计的信息,可以参考 Icon Design Guidelines 。
在Android 3.2 上声明平板布局
对于第一代运行着Android 3.0的平板电脑,去声明平板布局,比较合适的一种方式就是将布局放到带有 xlarge配置限定符的目录下(比如,res/layout-xlarge/)。为了兼容其他类型平板电脑和屏幕尺寸,Android 3.2 引入了一种新的方式去指定这些资源。这项新技术是根据你的布局需要多大的一个空间容量来定的(比如 600dp 的宽度),而不是尽量让你的布局去适配那几个尺寸类别(比如 large 或 xlarge)。
设计 7 英寸平板的理由是让人难以理解的,因为当使用尺寸类别去分类的时候,7 英寸和5英寸是同处于一个类别中(large类)。尽管这两个设备在尺寸上很相近,但从一个应用 UI 可用空间的角度来说,它却是有着一个很明显的区别。因此,对于 7英寸和5英寸的屏幕,它们不应该都用同一个布局。为了能够让你对这两种屏幕提供一个不同的布局,你可以根据应用的布局的宽或高去指定你的布局资源,这个宽高是以 dp 为单位来进行指定的。
举个例子,当你设计好了一个针对平板样式设备的布局之后,你也许想要让这个布局只有在大于 600dp 宽度的设备上,其才能运行, 这个门槛也就成了你平板布局的最小尺寸。那么现在,你可以指定这些布局资源让其只有在大于 600dp 宽的情况下,其才能够使用。
你要么可以选择一个宽度,然后将其作为一个最小尺寸宽度去设计布局,要么在布局编写完之后,测试你的布局所支持的最小宽度去作为平板布局的最小尺寸。
须知:
记住,使用这个新尺寸APIs的所有这些数字,其都是以dp 作为单位的,然后布局的尺寸也应以 dp 单位。更多关于密度无关像素信息可以参考上边的“术语和概念” 一节。
使用新的尺寸限定符
在下边的 table 2表中,总结了不同的资源配置。而这些配置都是依据布局的可用空间来进行指定。相比于传统的屏幕尺寸分类,这些新的限定符让你针对特定屏幕可以有更多的控制。
须知:
使用限定符指定的这些尺寸并不代表屏幕的实际尺寸,而是代表你的 activity 窗体可用的宽或者高( 宽或高的大小以 dp 为单位)。Android 系统可能会因一些系统UI而占据一部分屏幕(比如屏幕底部的系统栏,或屏幕顶部的状态栏),因此屏幕的某些部分可能就不能为你的布局所使用。因此,你所声明的这个尺寸指定的是你Activity所能用的一个尺寸。但需要知道的一点就是:尽管你的布局并没有去声明Action Bar,但是 Action Bar 还是算作你应用窗体的一部分的,所以你布局可用的空间就减少了,然后在设计布局的时候,你也必须把Action Bar所占据的空间算在你的布局内。
表2 针对屏幕尺寸的新配置限定符 (Android 3.2 引入的)
尽管这些限定符的使用看起来比去使用屏幕尺寸分类限定符更为复杂,但是一旦你确定了用户界面的需求之后,它实际上是较为简单一些的。在你设计 UI 的时候,你可能比较关注的一件事就是:在手机风格的UI和平板风格的UI进行切换时,这个切换的实际尺寸应该是多少的问题。而关于这个切换的关键点则是依赖于你特定的设计,也许对于平板布局,你需要让其达到 720dp才让其切换到平板风格。也许 600dp 就足以了,或者 480dp,或者在这些数之间。 使用 表2 中的限定符,你可以很精确地控制你的布局让其在什么去进行切换操作。
更多关于尺寸配置限定符,可以参考 Providing Resources
配置示例
为了能够帮助你在针对一些不同类型设备的时候去做进行一些设计,这里给出了一些典型的屏幕宽度的参数:
- 320dp: a typical phone screen (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc).
- 480dp: a tweener tablet like the Streak (480x800 mdpi).
- 600dp: a 7” tablet (600x1024 mdpi).
- 720dp: a 10” tablet (720x1280 mdpi, 800x1280 mdpi, etc).
res/layout/main_activity.xml # For handsets
res/layout-sw600dp/main_activity.xml # For tablets
在这种情况下,只有屏幕的最小可用宽度达到 600dp 以上,平板布局才会被使用。
例如如果你想要针对7英寸和10英寸去定义不同的布局,那么你可以再增加一个最小宽度的布局:
res/layout/main_activity.xml # For handsets (smaller than 600dp available width)
res/layout-sw600dp/main_activity.xml # For 7” tablets (600dp wide and bigger)
res/layout-sw720dp/main_activity.xml # For 10” tablets (720dp wide and bigger)
注意,前面两个示例使用了“最小宽度”的限定符,sw<N>dp,它是用来指定屏幕两边的一个最小尺寸,与当前屏幕是什么方向是没有关系的。
(译者附加备注:
这个最小尺寸,指的是:将屏幕左右两边计算的宽度与上下两边计算的高度进行一个比较,取两者中最小值作为这个最小尺寸,换言之,也就是必须得保证这个屏幕的宽和高都得在这个值之上,设备才会去使用这个布局)
然而,在某些时候,明白你的布局当前真正可用的宽或高的大小是非常重要的。举个例子,如果你的布局是由两个fragment并排组成的,你也许想要在 屏幕宽度为 600dp 以上时去使用这个布局,而无论此时的设备是处于横屏还是竖屏。那么,在这种情况下,你的资源应该类似下面这样:
res/layout/main_activity.xml # For handsets (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml # Multi-pane (any screen with 600dp available width or more)
注意:
第二种设置里边使用了“可用宽度”限定符,w<N>dp 。在这种方式下,一个设备可能两种布局都使用得到,它取决于屏幕的一个方向(如果在一个屏幕方向上,它的宽度就已达到了600dp以上,而在另一个屏幕方向上,其宽度小于600dp)。
如果你用可用高度来进行衡量,那么同理,你也可以使用h<N>dp 限定符,如果有需要的话,你还可以使用 w<N>dp和 h<N>dp这两个限定符的组合。
声明适配的屏幕尺寸
在你对不同的屏幕尺寸去填充完布局之后,在清单文件(Manifest file)中去声明你应用所能适配的屏幕尺寸也是同样重要的。
伴随新的屏幕尺寸限定符,在Android 3.2 中也引入了针对清单节点 <supports-screens>的新属性 :
android:requiresSmallestWidthDp
用来指定这个最小宽度值。这个最小宽度尺寸 (以 dp 为单位) 必须是你应用 UI 可用的一个屏幕空间尺寸,也就是,屏幕可用的宽和可用的高之间较小的那个值。所以为了能够让一个设备去兼容你的应用,那么这个设备的最小宽度必须大于或等于这个值。(通常,这个值是你布局所能适配的最小宽度值,与屏幕当前的方向无关)
举个例子,如果你的应用仅仅只支持最小可用宽度为 600dp以上的平板风格的设备:
<manifest ... ><supports-screens android:requiresSmallestWidthDp="600" />...
</manifest>
但是,如果你的应用想要适配Android所支持的所有的屏幕尺寸(最小可达 426dp * 320dp),那么,你就没有必要去声明这个
属性,因为你应用的最小可用宽度比任何设备的最小可用宽度都要小。
android:compatibleWidthLimitDp
通过指定你应用所能适配的一个 “最小宽度” 的最大值,这能够让你去使能一个用户可选的特性:screen compatibility mode。
如果设备的最小可用宽度大于这个值,用户依然可以安装这个应用,但是它能够选择在屏幕兼容模式下去运行。默认情况下,屏幕兼容模式是处于禁止状态的,你的布局也依然能够重新调整大小以适应屏幕,但是系统栏的按钮将变得可用,能够让用户去打开或者关闭掉屏幕兼容模式。
须知:
如果你应用的布局还需针对 large屏幕去做一个合适的调整,你就没有必要再去使用这个属性。我们推荐你用本章下边的一些建议去让你的布局针对 large 屏幕做一个合适的调整,而不再是使用这个属性。
android:largestWidthLimitDp
通过指定“最小宽度”的最大值,这个属性可以强制使能 screen compatibility mode 。如果你设备可用屏幕最小的一边大于这个值,那么系统就会运行在屏幕兼容模式下,并且此时用户也没法去禁止屏幕兼容模式。
须知:
如果你应用的布局还需针对 large 屏幕去做一个合适的调整,你就没有必要再去使用这个属性。我们推荐你用本章下边的一些建议去让你的布局针对 large 屏幕做一个合适的调整,而不再是使用这个属性。
注意:
当在Android 3.2版本或者更高版本上进行开发时,你不要将一些旧的屏幕尺寸属性与上述那些属性结合起来使用。当结合起立使用时,可能会造成一些问题。
更多关于每个属性的信息,可以参考上述相应的连接
最佳实践
多屏幕适配的目标就是让创建的应用能工作正常,还能让应用在每种类别的屏幕配置上看起来都比较美观。前面的那些部分主要是讲述的是应用如何去适配屏幕。这一部分将给出一些其他的建议以及一份有助于让你的应用去适配不同屏幕配置的技术总结。
以下是一份简要的清单,主要讲述如何确保你的应用能够在不同屏幕上显示正常。
1>在 XML 布局文件中用 wrap_content
, match_parent
,或 dp
单位去指定尺寸
2>在应用的代码中,不要使用固定的像素值
3>不要使用绝对布局 (它已经过时了)
4>针对不同屏幕密度提供不同的位图图片
以下这一部分进行了更为详细的讲述
1.对于布局尺寸,使用 wrap_content,match_parent 或者 dp单位
当在 XML 布局文件中为控件去定义 android:layout_width 或者 android_height时,使用"wrap_content", "match_parent" 或 dp单位,主要是为了控件尺寸能够很好地与当前设备的屏幕进行匹配。举个例子,对于一个 layout_width="100dp"的控件,在中等密度的屏幕上,以为着 100个像素值,在高密度的屏幕上,系统就会将其放大至 150个像素值,所以在屏幕上,依旧占据着相同的物理尺寸。
与之类似,你应该使用 sp 去定义字体的大小。sp 放大的倍数则取决于用户的设置,然后系统会将这个尺寸进行缩放,与dp 一样。
2.在应用的代码中不要使用固定的像素值
考虑到性能因素以及为了让代码更简洁,Android 系统使用像素作为 标准单位去表示尺寸和坐标值。这意味着在代码中,一个控件的尺寸总是基于当前屏幕密度的像素值去表示的。举个例子,如果 myView.getWidth() 返回10,那么这个控件在当前屏幕下,其宽度是 10 个像素,当在一个更高密度的屏幕上时,这个值就有可能返回 15。如果你使用这个像素值去处理当前屏幕下那些没有缩放过的位图,那么你可能就需要修改这个像素值以匹配源位图。关于应用在运行期间处理位图或者处理像素值,可以参考下面的这个部分 Additional Density Considerations
3.不要使用绝对布局
不像其他布局组件,绝对布局强制使用固定的位置去放置它的子控件,这容易会导致用户界面在不同显示器上工作不正常。因为这一点,所以在 Android 1.5 (API Level 3)的时候,就将这个绝对布局启用了。
你应该使用相对布局来替代绝对布局,相对布局是使用一个相对的位置去放置它的子布局的。例如,你可以指定一个 button控件 让其 居于 一个 text 控件的右边。
4.使用尺寸资源和特定密度资源
尽管系统会基于当前屏幕配置去缩放你的布局和图片资源,但你也许还想要针对不同屏幕尺寸,去做一些UI的调整以及针对不同屏幕密度去对位图图片做一个优化。这些信息在上面章节中已有重述。
如果你想要在各种屏幕配置上都能对你应用的显示进行精准地控制,那么你就需要通过配置限定符资源目录去调整你的布局以及位图图片。举个例子,思考这么一张图标,你想要让它显示在中等屏幕密度和高屏幕密度上。仅仅只需要创建两种尺寸的图标(例如 为中等屏幕密度去设计一张100*100 图片,为高屏幕密度设计 150*150),然后将这两张图片放置到有对应限定符修饰的目录下:
res/drawable-mdpi/icon.png //for medium-density screens
res/drawable-hdpi/icon.png //for high-density screens
须知:
如果在目录名中没有密度限定符修饰,那么系统会认为这个目录的资源是针对中等屏幕密度的,然后在遇到其他屏幕密度时,系统会将其进行一个合适的缩放。
关于密度的注意事项
这个部分更多的是讲述Android 如何在不同屏幕密度上去缩放位图图片的,以及在不同屏幕密度上,你能怎样控制位图的绘制。对于大多数应用来说,这一部分显得就不是那么重要了,除非你的应用运行在不同屏幕密度或者在操控图形上遇到问题时,可能就需要参考一下这个部分。
为了更好地理解,在运行期间操控图片时,系统是如何适配多种屏幕密度,你需要知道系统会通过以下这些方式去确保位图缩放到一个合适的效果:
1. 资源的预缩放(比如位图图片)
系统会根据当前屏幕密度,使用对应尺寸和密度的资源,将它们原样显示出来(不会进行缩放)。如果在当前屏幕密度下,没有对应密度的资源,那么系统会去加载这个默认的资源,然后放大或缩小资源以匹配当前屏幕密度。系统会认为这些默认的资源都是基于中等屏幕密度的(mdpi)而设计的。当需要在当前屏幕密度下将图片调整到一个合适尺寸时,系统会通过预缩放去做这件事。
如果你想要知道这个预缩放过的资源的尺寸,那么系统会将缩放后的尺寸值予以返回。举个例子,对于一个在中等屏幕密度下的 50* 50像素的位图,若把它放在高屏幕密度上时,就会被放大为一个 75*75 个像素的位图(如果没有可供选择的 hdpi 资源),然后系统会去报告 75*75个像素的这个值。
这里有一些解决方案用来解决那些你不想让Android进行上缩放的资源。阻止预缩放最简单的一种方式就是使用 nodpi
配置限定符去修饰资源目录。例如:
res/drawable-nodpi/icon.png
当系统使用 这个文件夹下的 icon.png 时,它将不会根据当前设备密度去进行缩放。
2. 像素尺寸和坐标的自动缩放
应用禁止的预缩放的方式有两种,第一种方式就是通过在 manifest 中将 android:anyDensity 设置为 “false” ,
时候去自动缩放绝对的像素坐标和像素尺寸值。这样做的目的主要是为了确保用像素定义的控件在中等屏幕密度中显示的物理尺寸与他们原本在中等屏幕中应显示的物理尺寸一样。系统的缩放处理,对应用里说,是透明的,系统会将缩放后的尺寸上报给应用。
举个例子,假设一个 480*800 的WVGA高密度屏幕,其屏幕尺寸和传统的 HVGA 屏幕尺寸一样,但是在运行的时候,
其把预缩放给禁止掉了。在这种情况下,当应用要查询屏幕尺寸的时候,系统会谎报给应用一个 320*533 的值(这是将转化为中等屏幕密度后的值作为了当前这个屏幕密度的值)。然后,当应用执行绘制操作时,比如要绘制一个从 (10,10)到(100,100)的矩形,而实际上他们绘制的区域却为(15,15)到(150,150)。如果你的应用直接操控这种经缩放过的位图,那么它有可能会出现一些无法预测的行为,但这是为了应用能够较好地执行而采取的比较合理的一种方法。如果你遇到这种情况,你可以阅读这个部分 Converting dp units to pixel units.
通常,你不应该去禁止预缩放。适配屏幕最好的方法可以参考上边的 How to Support Multiple Screens.
如果你的应用在屏幕上进行位图的绘制或以其他方式直接与像素进行交互,你应该采取其他一些手段以支持不同屏幕密度。举个例子,如果你通过手指划过的像素数去响应触控手势,你应该使用密度无关像素去替代真正的像素。
缩放动态创建的位图对象
如果你的应用动态地创建了一个的位图,那么就系统会认为这个位图是基于中等屏幕密度而设计的,默认地,会在
绘制期间自动去缩放这个位图。当一个位图没有指定密度属性时,系统会利用“自动缩放”去缩放这个位图。如果你没有说明当前设备的屏幕密度以及没有指定这个位图的密度属性,那么自动缩放会造成一些缩放效应,这和你没有
提供可供选择资源的效果是一样的。
为了控制一个动态创建的位图是否需要缩放,你可以用 setDensity()
通过传出入一个DisplayMetrics
的密度常量
DENSITY_HIGH
或 DENSITY_LOW
去指定这个位图的密度。
如果你使用 BitmapFactory
去创建一个位图,比如一个从一个文件或者流中,那么当它已经存在了的时候,你可以
使用
去定义这个位图的属性,这将会导致系统是否或者如何去缩放这个位图。例如,当你使用 BitmapFactory.Options
inDensity
属性去说明这个位图是为哪种屏幕密度而设计的,而inScaled 这个属性使用指定这个位图是否应该缩
放去匹配设备的屏幕密度。
如果你设置 inScaled 属性为 false,那么预缩放也就被禁止了,此时系统会在绘制期间使用自动缩放去缩放
这个位图。使用自动缩放来替代预缩放会加重CPU的载荷,但是减少了内存的消耗。
图5 预缩放和自动缩放位图比较
图5阐述了:当在高密度屏幕上加载 低密度(120),中等密度(160),高密度(240)位图时,预缩放和自动缩放的结果。它们之间的不同非常细微的,因为所有的位图都被缩放用来去匹配当前屏幕密度,但是在
运行期间,预缩放的图片和缩放的图片在外观上还是有一些细微的区别。
因为图形框架的改善,所以在Android 3.0 及 以上,预缩放和自动缩放在肉眼上是察觉不出它们之间有什么
区别的。
dp单位到像素单位的转化
在某些情况下,你需要将 dp 修饰的尺寸转化为 像素。想象这么一个应用,在这个应用里边,只有当用户的手指移动16 个像素时,其滑动的手势才能被识别。那么在中等密度的屏幕中,用户必须滑动 16 pixels / 160 dpi ,也就是 1/10 英寸(2.5mm)才能识别出这个手势。一旦到高屏幕密度的设备中(240dpi),用户必须滑动 16 pixels / 240 dpi, 也就是 1/15 英寸(1.7mm)。这个距离就更短,应用也因此需要对此更加敏感。
为了解决这个问题,那么这个手势的临界值在代码中必须以 dp 的形式去表达,然后再将其转化为具体的像素值。
举个例子:
// The gesture threshold expressed in dp
private static final float GESTURE_THRESHOLD_DP = 16.0f;// Get the screen's density scale
final float scale = getResources()
.getDisplayMetrics()
.density;
// Convert the dps to pixels, based on density scale
mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);// Use mGestureThreshold as a distance in pixels...
信息,可以参考 DisplayMetrics 类。
但是,对于这种手势检测事件,你应该用 ViewConfiguration 里边预缩放配置的值,而不是自己去定义一个任意的
临界值。
使用预缩放配置值
滑动临界值可以通过 getScaledTouchSlop()
获取到:
private static final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();在ViewConfiguration 里边,以 getScaled开头的方法,其返回值都是以像素(px)为单位返回的,并且无论当前屏幕密度是多少,这个值都能正确地进行显示。
如何测试应用的屏幕适配
emulator -avd <avd_name> -scale 96dpi
为了修改模拟器尺寸,你可以给 -scale 选项传递一个 0.1 到 3之间的一个数,这个数就表示你所想要缩放的一个比例。
参考:
《Supporting Multiple Screens》
这篇关于Anddroid 适配多种屏幕的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!