原文链接:Supporting Multiple Screens
Android运行在许多不同屏幕尺寸(screen size)和密度(screen density)的设备上。对于应用程序,Android系统提供了一致的跨设备开发环境,处理了应用程序适配屏幕的大部分工作。与此同时,为了优化不同屏幕配置的UI设计,系统还提供了API让你的应用程序为特定的屏幕尺寸和密度做控制。例如,你可能想平板的UI和手机的不一样。
虽然系统会通过缩放(scaling)和调整尺寸来使你的应用程序工作在不同的屏幕上,但你也应该努力在不同的屏幕上进行优化。这么做,你就能为所有设备的用户体验最优化,你的用户也就相信你的应用程序是为了他们的设备设计的,这比简单的拉伸去适应他们的设备好多了。
注意:这篇文档假设你的应用程序是为Android 1.6 (API Level 4) 或者更高版本设计的,如果你的应用程序要支持Android 1.5 或者更低的版本,请先阅读Android 1.5 屏幕适配策略。
另外,注意Android 3.2有提供新的API去让你更精确去控制应用程序对于不同屏幕尺寸的布局资源(layout resources)。这些新的特性会让你的应用程序在为平板优化是显得特别重要。更多的细节,会在"Declaring Tablet Layouts for Android 3.2"这一小节提到。
1:屏幕支持概述
这一小节概述了Android对不同屏幕的支持,包括对该文档和API中出现的名词和概念的介绍,对系统支持的有关屏幕配置(screen configurations)和相关API,还有屏幕兼容的底层特性。
1.1:名字和概念
屏幕尺寸(Screen size)
实际的物理尺寸,根据屏幕的对角线进行测量。
为了简单起见,Android归类了所有实际的屏幕尺寸为四种泛化的尺寸: small , normal , large,extra large。
屏幕密度(Screen density)
屏幕中一个物理区域包含的像素数;通常和dpi(dots per inch)相对应。例如,一个"low"密度屏幕比一个"normal"或者"high"密度的屏幕有更多的像素。
为了简单,Android归类了所有实际屏幕密度为四种泛化的密度:low, medium, high, extra high。
方向(Orientation)
从用户角度看的屏幕方向。这里指的是横屏(landscape)或者竖屏(portrait),意味着屏幕高宽比(aspect ratio)分别是宽的或者是高的。注意不同设备的屏幕不只是有默认的屏幕方向,它们也可能在运行时用户翻转屏幕发生改变。
分辨率(Resolution)
屏幕物理像素的总数,当进行多屏幕的支持时,程序不必和分辨率直接打交道;程序应该只关心屏幕的尺寸和密度,如上面指定的几个泛化尺寸和密度归类。
密度-独立像素(Density-independent pixel [dp])
虚拟像素单位,也是你应该在定义UI布局,表达尺寸和位置时使用的一种密度独立方式。
在160dpi屏(也就是"medium"密度的屏幕的基线),一个密度独立像素(dp)等于一个物理像素(px)。在运行时,系统会根据实际的屏幕密度去透明地处理dp单位的缩放比例。dp单位和px单位装换非常简单:
px = dp * (dpi /160).
例如:在240dpi的屏幕上,1dp = 1.5px。你应该总是使用dp去定义应用UI的大小,从而可以保证在不同的密度屏幕上显示你的UI。
1.2:支持的屏幕范围
从Android 1.6(API Level 4)开始,Android为不同的屏幕尺寸和密度提供了支持,反映了设备可能有的多种不同的屏幕配置(screen configuration)。你可以使用Android系统的特性(feature)去为每个屏幕配置优化你应用程序的UI,还可以保证你的程序UI不仅合理得渲染,而且为每个用户的屏幕都尽可能提供最好的用户体验。
为了简化多屏幕的UI设计,Android把实际的屏幕尺寸和密度划分为:
四种泛化的尺寸: small ,normal, large, xlarge 注意:从Android3.2(API level 13)开始,这种尺寸划分已经过时了,取代的是一种管理屏幕尺寸和密度的新技术。如果你在开发Android 3.2或者更高的版本,看篇文章获取更多信息Declaring Tablet Layouts for Android 3.2.
四种泛化的密度: ldpi(low),mdpi(medium),hdpi(high),xhdpi(extra high)
在基准配置(baseline configuration)附近的泛化屏幕尺寸(generalized size)是normal,泛化屏幕密度(generalized density)是mdpi。这个基准线是建立在首部Android驱动的手机(T-Mobile G1)的屏幕配置(screen configuration)上的,它有一个HVGA屏(这是Android在1.6版本之前唯一支持的屏幕配置)。
每一种泛化尺寸和密度对应于实际屏幕尺寸和密度的一个范围。例如,两台声明有一样的normal屏幕尺寸的设备,它的实际屏幕尺寸和宽高比(aspect ratio)可能有点不一样。相似地,同样声明一样hdpi的屏幕密度的设备,它的实际像素密度也可能有点不一样。Android 在应用程序中抽象了这些轻微的不一致,所以你只需要为泛化的尺寸提供UI设计,让系统去处理必要的调整即可。图一解释了不同的尺寸和密度是如何归类的。
图 1
当你在为不同屏幕尺寸设计UI时,你会发现每种设计都需要一个最小空间。所以,每种泛化屏幕尺寸之上会关联上系统定义的最小分辨率。这些最小尺寸的单位是“dp”(你应该在定义布局时使用它,可以让系统免于担心屏幕密度的改变问题)。
xlarge 的最小空间为 960dp x 720dp
large 的最小空间为 640dp x 480dp
normal的最小空间为 470dp x 320dp
small 的最小空间为 426dp x 320dp
注意:在Android 3.0前后的最小屏幕尺寸定义是不一样的。所以你可能会碰到一些在normal和large的设备错分的现象。这些也会建立在屏幕的物理分辨率上,所以会在跨屏幕上造成不同,例如一个带有系统栏的1024 x 720的 平板实际上对于应用程序会稍微少一点可用空间,因为这部分空间已经被系统栏占用了。
要为不同的屏幕尺优化你的应用程序UI,你可以为不同的泛化尺寸和密度提供可选资源(alternative resources)。典型地,你应该提供可选择的布局(layouts)给不同的屏幕大小和可选择的位图图片给不同的屏幕密度。 在运行时,系统会根据当前的设备屏幕的泛化尺寸和密度去选择合适的资源。
你没有必要为每种屏幕尺寸和密度的组合都提供一种可选资源。系统提供了非常健壮的兼容特性(compatibility feature)去处理应用程序在不同设备上渲染的大部分工作,通过优雅地调整(resize)尺寸的技术来实现你的UI。(下面的小节"最佳实践"上会进行描述)
注意:定义设备的泛化屏幕尺寸和密度的特征是相互独立的。例如:一个WVGA的高密度屏幕(high-density)被认为是一个normal尺寸的屏幕因为它的物理尺寸和 T-Mobile G1的差不多(Android的首台设备和基准屏幕配置)。在另一方面,一个WVGA的中等密度屏幕(medium-denity)会认为是large尺寸屏幕。虽然它们都有相同的分辨率(resolution,一样数量的像素),WVGA的中等密度屏幕的屏幕密度更低,意味着每个像素的物理尺寸要大些,因此整个屏幕也要比基准(normal)屏幕要大。
1.3:密度独立(Density independence)
当UI元素在不同密度的屏幕上呈现时,程序会通过“像素独立(density independence)”来维护它的物理尺寸(从用户的角度)。
维护密度独立是非常重要,因为如果没有它,UI元素(比如按钮)会在低密度(low density)的屏幕上的物理呈现要比高密度(high density)的大。这样的密度相关(density-related)尺寸改变会导致应用程序的布局和可用性问题。图2和3分别向我们展示了这些不同。
图2:不支持不同密度的情况,从左往右分别是 low, medium, high density的屏幕。
图3:支持不同密度的情况,从左往右分别是 low, medium, high density的屏幕。
Android 系统通过两种方式去帮助你的应用程序完成密度独立:
系统在当前屏幕密度下适当地缩放dp单位
系统在当前屏幕密度下适当地缩放绘制资源(drawable resource)
在图2中,text view 和 bitmap drawable的维度(dimension)制定为像素(px units),所以这些视图在低密度的屏幕上比高密度的屏幕要大。这是因为,尽管它们的实际屏幕尺寸一样,但高密度的屏幕在每英寸上拥有更多的像素(同样的像素占用更少的面积)。在图3中,布局的维度指定为密度独立(density-independent)像素(dp units)。因为密度独立的基准线是个中等密度(medium-density)屏幕,所以带有中等密度屏幕的设备就像图2一样拥有一致的视觉效果。然而,对于低密度和高密度的屏幕,系统会相应地缩放密度独立像素去适应屏幕。
在大部分情况下,你可以通过简单地在程序中使用dp单位或者"wrap_content"来保证密度独立。系统会适当地缩放bitmap drawable显示在不同的尺寸上,这是建立在当前屏幕密度的合适的缩放因子基础上的。
然而,位图的缩放可能会导致模糊或者出现马赛克,你可能会在屏幕截图上注意到这一点。为了避免这些现象,你应该为不同的密度提供可选的位图资源(alternative bitmap resource)。例如,你应该为高密度(high-density)的屏幕提供更高分辨率的位图,系统会使用这些图片而不是去调整为中等密度(medium-density)屏幕设计的位图。接下来的小节将会描述更多关于如何为不同的屏幕配置提供可选择的资源。
2:如何支持多屏幕
Android对于多屏幕的支持基础是它能够管理应用程序的布局(layout)和位图(bitmap drawables)的渲染,使得能在当前屏幕配置下已一种合理的方式工作。系统通过缩放布局去适应屏幕尺寸/密度和缩放位图去适应屏幕密度的方式,来负责处理应用程序在不同屏幕配置下的渲染工作。然而,为了更好地处理不同的屏幕配置,你还应该:
(1)显式地在manifest文件中声明你的应用程序要支持那个屏幕尺寸
通过声明你的应用程序支持的屏幕尺寸,你可以保证只有你支持的设备能够下载你的应用程序。声明对不同的屏幕尺寸的支持也会影响系统在更大屏幕上绘制你的应用程序,特别地,不论你的应用程序是否在屏幕兼容模式(screen compatibility mode)下。
去声明应用程序支持的尺寸,你应该包含元素到你的manifest文件中。
(2)为不同的屏幕尺寸提供不同的布局
默认地,Android会重新调整你的应用程序布局大小去适应当前的设备屏幕。在大部分情况下,这都可以很好地工作。但在有些情况下,你的UI可能看起来就没有那么好了,这时候就需要为不同屏幕尺寸做调整了。例如,在一个大屏幕上,你可能想去调整一些元素的位置和尺寸来利用额外的屏幕空间,或者在一个更小的屏幕上,你可能想调整大小去让所有元素都能合适地放在屏幕上。
你可以为特定尺寸指定资源使用的配置限定词(configuration qualifiers)有small,normal,large和xlarge。例如,对于特大屏幕的布局应该放在layout-xlarge/里面。
从Android 3.2 (API level 13)开始,以上的尺寸分类已经过时了,取而代之的是使用swdp配置限定词去定义你的布局资源需要的最小有效宽度。例如,如果你的多面板(multi-pane)平板布局需要至少600dp的屏幕宽度,你应该把相应的资源放到layout-sw600dp/里面。更多关于使用新的技术去声明布局资源在 Declaring Tablet Layouts for Android 3.2小节中。
(3)为不同的屏幕密度提供不同的位图资源
默认地,为了可以在每台设备上渲染出合适的物理尺寸,Android会缩放你的位图(.png,,jpg,and .gif 文件) 和 .9图片(Nine-Patch)。例如,如果你的应用程序仅为基准的中等密度屏幕(mdpi)提供了图片,那么系统会在高密度(high-density)屏幕相应地放大,在低密度(low-density)的屏幕上缩小。这些缩放会给图片带来影响。为了让你的图片看起来更舒服,你应该为不同的屏幕密度提供不同分辨率的可选版本。
你可以为特定密度指定资源使用的配置限定词有ldpi(low),mdpi(medium),hdpi(high)和xhdpi(extra high)。例如,在高密度屏幕上的图片应该放到drawable-hdpi/文件夹中。
屏幕尺寸和密度配置限定词对应的泛化尺寸和密度在上面的Range of screens supported小节有讨论。
注意:如果你对于配置限定词和系统是如何把它们应用到可选资源上还不是很熟悉,可以阅读Providing Alternative Resources获取更多信息。
在运行时,系统会通过下面的步骤来保证资源能够尽可能好地显示在当前屏幕上:
1:系统使用合适的可选资源(alternative resource)
基于当前屏幕的尺寸和密度,系统会在你应用程序中提供的任何 size- 或者 density- 指定的资源。例如,如果设备是个高密度的屏幕,应用程序在请求一个drawable资源,系统会须按照一个最匹配屏幕配置的资源目录。取决于其他有效的可选资源,一个具有hdpi限定词的资源目录(比如drawable-hdpi/)可能是最匹配的,因此系统会从这个目录中选用drawable资源。
2:如果不存在有效的匹配资源,系统会使用默认的资源,然后通过放大或缩小它来满足当前的屏幕大小。
“默认”的资源是那些没有配置限定符标志的。例如,在drawable/目录下是默认的drawable资源。系统假定这些资源是为基准屏幕尺寸和密度而设计的,也就是normal尺寸和medium密度的屏幕。如此,系统会为high-density的屏幕适当地放大默认密度的资源,反之low-density的屏幕则会进行缩小。 然而,当系统在寻找特定密度(density-specified)的资源而在特定密度的目录下找不到时,它不总是会使用默认的资源的。系统可能会代替使用其他的特定资源去进行缩放以得到更好的结果。例如,当找不到low-density的资源时,系统更喜欢去缩小high-density的资源,因为系统可以轻易的把high-density中的资源按照0.5的因子去缩小它去适应low-density的版本,这比起使用medium-density资源要0.75的缩放因子要更好些。
更多关于Android如何为屏幕配置去选择不同配置限定符资源的信息请阅读How Android Finds the Best-matching Resource
2.1:使用配置限定符(configuration qualifier)
Android支持多种配置限定符去让你控制系统使用可选的资源。配置限定符是跟在Android项目里面资源目录后的一个字符串。
使用配置限定符,你需要:
1:在你的项目res/目录下创建一个线的目录并使用这样的命名格式.
- 是一个标准的资源名(比如drawable 或者 layout)
- 是表格1中的其中一个配置限定词,指定每个屏幕配置会使用到的资源。
2:在这个新的目录下保存一个合适特定配置的资源。资源文件的名字必须和原来默认的一样。
例如:xlarge是特大屏幕的配置限定词。当你在资源目录上加上这个字符串(比如layout-xlarge),系统就会在那些特大屏幕的设备上使用这些资源了。
表1. 为不同屏幕配置提供的配置限定符。
注意:如果你在开发Android 3.2或者更高版本的应用程序,请关注这篇文章Declaring Tablet Layouts for Android 3.2获取为特定屏幕尺寸声明布局资源该使用的配置限定符。
更多关于这些限定符怎么粗略地对应上真实的屏幕尺寸和密度,可以看这篇文章:Range of Screens Supported,前面已经提到过了。
例如,下面在程序中的资源目录列表分别为不同屏幕尺寸提供不同的布局设计和为不同的屏幕密度提供了不同的图片。
res/layout/my_layout.xml // layout for normal screen size ("default")
res/layout-small/my_layout.xml // layout for small screen size
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 orientation
res/drawable-mdpi/my_icon.png // bitmap for medium density
res/drawable-hdpi/my_icon.png // bitmap for high density
res/drawable-xhdpi/my_icon.png // bitmap for extra high density
获取更多关于怎么使用可选资源和完整的配置限定符列表(不只是屏幕配置),请看 Providing Alternative Resources这篇文章。
值得注意的是,当Android系统在运行时选择资源时,它会按照某种逻辑去决定哪个是”最匹配“(best matching)的资源。也就是,你没有必要为每一种屏幕配置都配备一份资源。特别地,当基于尺寸限定词(size qualifier)选择资源时,系统会使用比较小的那个资源如果没有更合适的。(比如,如果有需要,一个large-size的屏幕会使用normal-size的资源)。然而,如果资源只存在比当前屏幕尺寸更大的限定符中,系统将不会使用它,你的程序将会崩溃如果没有其他符合设备配置的资源(比如,如果所有的资源都放到xlarge限定符的目录下,但是设备是normal-size的屏幕)。更多信息关于系统如何选择资源,请阅读How Android Finds the Best-matching Resource。
提示:如果你有些图片资源是不希望被缩放的(可能因为你想在运行时自己去调整),你应该把它们放到nodpi的配置限定符目录下。在这个限定符下的资源被认为是密度不可知的(density-agnostic),系统不会去缩放它们。
2.2:设计可替换的布局和图片(drawables)
你要创建的可选资源类型取决于你程序的需要。通常,你应该使用尺寸和方向限定符去提供可选布局资源,用密度限定符去提供可选图片资源。
下面的小节分别总结了该如何使用尺寸和密度限定符去提供可选布局或者图片。
可选布局(alternative layouts)
一般来说,你在不同屏幕配置下测试应用程序时,会知道是否应该为不同的屏幕尺寸提供可选布局。比如:
当在小屏幕上测试时,你可能发现你的布局不是很合适。比如,在小屏幕设备上的一排按钮可能不太合适屏幕的宽度。在这种情况下,你应该为小屏幕提供可选布局去调整尺寸或者按钮的位置。
当在特大屏幕上测试时,你可能发现你的布局没有有效地利用大屏幕而出现了明显的拉伸现象。在这种情况下,你应该为特大屏幕提供为它重新设计优化的UI的可选资源布局。尽管你的程序在没有为大屏提供可选布局的情况下也可能工作地很好,但是让用户看起来这个程序是为他们的设备而设计的这点是非常重要的。如果UI很明显有拉伸,用户可能不会满意程序的体验。
还有,在对比测试横屏和竖屏时,你可能会注意到在竖屏的底部的UI元素,在横屏上应该放置在右侧。
总结一下,你应该保证你的程序布局:
适用于小屏幕(这样用户能正常适用你的程序)
为大屏幕进行优化以利用额外的屏幕空间
为横屏和竖屏同时优化
如果你的UI使用图片在系统缩放布局之后还需要填充view的尺寸(例如按钮的背景),你应该使用.9图片文件。一个.9文件是建立在指定了两维的拉伸区域的PNG文件上的。当系统需要去缩放view使用的那个位图时,系统会拉伸.9图片特定的区域。这样,你就不必为不同的屏幕尺寸提供不同的图片了,因为.9图片可以调整成任何大小。然而,你应该为不同屏幕密度提供不同版本的.9图片。
可选图片(alternative drawables)
几乎所有的应用程序都应该有不同屏幕密度的可选图片资源,因为大部分应用程序有在全部屏幕密度下看上去漂亮的应用启动图标。同样,如果你的程序包含其他图片(比如在应用中的菜单图标或者其他图形),你应该为每个不同的密度提供一个可选择的版本。
注意:你仅需要为图片文件和.9文件提供不同密度的文件。如果是用XML来定义的图形,颜色或者其他drawable资源,你应该在默认的drawable目录(drawable/)下放一份拷贝。
为不同屏幕密度创建可选的图片,你应该在四种泛化密度上遵循 3 : 4 : 6 : 8 的缩放比例。比如,如果你为medium-density的屏幕提供一张48x48的图片(应用启动图片的尺寸),所有不同的尺寸应该是:
36x36 for low-density
48x48 for medium-density
72x72 for high-density
96x96 for extra high-density
更多关于如何设计图标的信息,可以看Icon Design Guidelines,里面包含了不同的图片尺寸信息,比如应用启动图标(launcher icons),菜单图标(menu icons),状态栏图标(status bar icons),切换卡图标(tab icons)和更多信息。