Android 设备的形状和尺寸多种多样,因此应用的布局需要十分灵活。也就是说,布局应该从容应对不同的屏幕尺寸和方向,而不是为布局定义刚性尺寸,假定屏幕尺寸和宽高比是一定的。
如果能够支持尽可能多的屏幕,您的应用就可以在各种不同设备上运行,这样您就可以使用单个 APK 将其提供给最多的用户。此外,如果能够使您的应用灵活适应不同的屏幕尺寸,可确保您的应用可以处理设备上的窗口配置更改,例如,当用户启用多窗口模式时发生的窗口配置更改。
本页将向您介绍如何采用以下技巧来支持不同的屏幕尺寸:
但请注意,适应不同的屏幕尺寸并不一定会使您的应用与所有 Android 设备类型兼容。您应该采取其他措施来支持 Android Wear、Android TV、Android Auto 和 Chrome 操作系统设备。 有关针对不同屏幕构建界面的设计指南,请参阅自适应界面的 Material 指南。
无论您首先要支持什么硬件配置文件,都需要创建一个能够灵活应对屏幕尺寸变化(即便是微小变化)的布局。
要创建适用于不同屏幕尺寸的自适应布局,最佳做法就是将 ConstraintLayout 用作界面中的基本布局。使用 ConstraintLayout,您可以根据布局中视图之间的空间关系来指定每个视图的位置和大小。通过这种方式,当屏幕尺寸改变时,所有视图都可以一起移动和拉伸。 要使用 ConstraintLayout 构建布局,最简单的方法就是使用 Android Studio 中的布局编辑器。借助该工具,您可以将新视图拖动到布局中、将其约束附加到父视图和其他同级视图,以及修改视图的属性,完全不必手动修改任何 XML(请参见图 1)。 有关详情,请参阅使用 ConstraintLayout 构建自适应界面。
图 1. Android Studio 中的布局编辑器,显示了一个 ConstraintLayout
文件
但是,ConstraintLayout
并不能解决所有布局场景(特别是动态加载的列表,对于此类布局,您应使用 RecyclerView),但无论您使用何种布局,都应该避免对布局尺寸进行硬编码(请参见下一节)。
为了确保布局能够灵活地适应不同的屏幕尺寸,您应该对大多数视图组件的宽度和高度使用 "wrap_content"
和 "match_parent"
,而不是硬编码的尺寸。
"wrap_content"
告知视图将尺寸设为让其能够容纳下相应内容所需的尺寸。
"match_parent"
使视图在父视图中尽可能地展开。
例如:
虽然此视图的实际布局取决于其父视图和任何同级视图中的其他属性,但是此 TextView
想要将其宽度设为填充所有可用空间 (match_parent
),并将其高度设为正好是文本长度所需的空间 (wrap_content
)。这样,此视图就可以适应不同的屏幕尺寸和不同的文本长度。
图 2 显示了当屏幕宽度随着设备屏幕方向而发生变化时,如何使用 "match_parent"
来调整文本视图的宽度。
图 2. 灵活的文本视图
如果您使用的是 LinearLayout,则也可以按布局权重展开子视图,以便每个视图按自身权重值所占的比例填充剩余的空间。但是,在嵌套的 LinearLayout 中使用权重将需要系统执行多次布局遍历来确定每个视图的尺寸,这会降低界面性能。幸运的是,LinearLayout 几乎能够构建 ConstraintLayout 所能构建的所有布局,而不会影响性能,因此您应该尝试将布局转换为 ConstraintLayout。然后,您就可以使用约束链来定义加权布局。 注意:使用 ConstraintLayout 时,不可使用 match_parent,而应将尺寸设为 0dp 以启用一种称为“匹配约束”的特殊行为,该行为通常与 match_parent 的预期行为相同。有关详情,请参阅如何调整 ConstraintLayout 中的视图尺寸。
虽然您的布局应始终通过拉伸其视图内部和周围的空间来应对不同的屏幕尺寸,但这样可能无法针对每一种屏幕尺寸提供最佳用户体验。例如,您为手机设计的界面或许无法在平板电脑上提供良好的体验。因此,您的应用还应提供备用布局资源,以针对特定屏幕尺寸优化界面设计。
图 3. 同一应用显示在尺寸不同的屏幕上,针对每种屏幕尺寸使用不同的布局
您可以通过创建额外的 res/layout/
目录来提供特定于屏幕的布局(针对需要不同布局的每种屏幕配置创建一个),然后将屏幕配置限定符附加到 layout
目录名称(例如,对于可用宽度为 600dp 的屏幕,附加限定符 layout-w600dp
)。
这些配置限定符表示应用界面可用的可见屏幕空间。从应用中选择布局时,系统会考虑所有系统装饰(例如导航栏)和窗口配置更改(例如,当用户启用多窗口模式时)。
要在 Android Studio(使用 3.0 或更高版本)中创建备用布局,请按以下步骤操作:
此时系统会在相应的布局目录中创建一个重复的布局文件,以便您可以开始自定义该屏幕变体的布局。
“最小宽度”屏幕尺寸限定符允许您为具有最小宽度(以密度无关像素 dp 或 dip 为度量单位)的屏幕提供备用布局。
通过将屏幕尺寸描述为密度无关像素的度量值,Android 允许您创建专为非常具体的屏幕尺寸而设计的布局,同时让您不必对不同的像素密度有任何担心。
例如,您可以创建一个名为 main_activity
且针对手机和平板电脑进行了优化的布局,方法是在目录中创建该文件的不同版本,如下所示:
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)
最小宽度限定符指定屏幕两侧的最小尺寸,而不考虑设备当前的屏幕方向,因此这是一种指定布局可用的整体屏幕尺寸的简单方法。
下面是其他最小宽度值与典型屏幕尺寸的对应关系:
图 4 提供了一个更详细的视图,说明了不同屏幕 dp 宽度与不同屏幕尺寸和方向的一般对应关系。
图 4. 建议的宽度断点以支持不同的屏幕尺寸
请记住,最小宽度限定符的所有数值都是密度无关像素,因为重要的是系统考虑像素密度(而不是原始像素分辨率)之后可用的屏幕空间量。
注意:您使用这些限定符指定的尺寸不是实际屏幕尺寸,而是 Activity 的窗口可用的宽度或高度(以 dp 为单位)。Android 系统可能会将部分屏幕用于系统界面(如屏幕底部的系统栏或顶部的状态栏),因此部分屏幕可能不可供您的布局使用。如果您的应用在多窗口模式下使用,则它只能使用该窗口的尺寸。该窗口进行大小调整时,会使用新窗口尺寸触发配置更改,以便系统可以选择适当的布局文件。因此,在声明尺寸时,您应具体说明 Activity 需要的尺寸。在声明为布局提供多少空间时,系统会考虑系统界面使用的所有空间。
您可能希望根据当前可用的宽度或高度来更改布局,而不是根据屏幕的最小宽度来更改布局。例如,如果您有一个双窗格布局,您可能希望在屏幕宽度至少为 600dp 时使用该布局,但屏幕宽度可能会根据设备的屏幕方向是横向还是纵向而发生变化。在这种情况下,您应使用“可用宽度”限定符,如下所示:
res/layout/main_activity.xml # For handsets (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml # For 7” tablets or any screen with 600dp
# available width (possibly landscape handsets)
如果您关心可用高度,则可使用“可用高度”限定符来执行同样的操作。例如,对于屏幕高度至少为 600dp 的屏幕,使用限定符 layout-h600dp
。
虽然您可能只需将“最小宽度”和“可用宽度”限定符结合使用就能支持所有尺寸变化,但是您可能还希望当用户的屏幕方向在纵向与横向之间切换时改变用户体验。
为此,您可以将 port
或 land
限定符添加到资源目录名称。只是需要确保这些限定符在其他尺寸限定符后面。例如:
res/layout/main_activity.xml # For handsets
res/layout-land/main_activity.xml # For handsets in landscape
res/layout-sw600dp/main_activity.xml # For 7” tablets
res/layout-sw600dp-land/main_activity.xml # For 7” tablets in landscape
如需详细了解所有屏幕配置限定符,请参阅提供资源指南中的表 2。
在针对多种屏幕尺寸设计应用时,您希望确保不会在 Activity 之间不必要地重复界面行为。因此,您应该使用Fragment将界面逻辑提取到单独的组件中。然后,您可以组合 Fragment 以便在大屏幕设备上运行时创建多窗格布局,或者在手机上运行时将 Fragment 放置在单独的 Activity 中。
例如,平板电脑上的一款新闻应用可能在左侧显示报道列表,而在右侧显示一篇完整的报道。在左侧选择一篇报道时,会更新右侧的报道视图。不过,在手机上,这两个组件应显示在单独的屏幕上。从列表中选择一篇报道时,会改变整个屏幕以显示这篇报道。
如需了解详情,请参阅使用 Fragment 构建动态界面。
如果您的应用支持 Android 3.1(API 级别 12)或更低版本,那么除了上面的最小/可用宽度限定符之外,您还需要使用旧尺寸限定符。
在上面的示例中,如果要在较大的设备上显示双窗格布局,那么您需要使用“large”配置限定符来支持 3.1 及更低版本。因此,要在这些旧版本上实现此类布局,您可能需要创建以下文件:
res/layout/main_activity.xml # For handsets (smaller than 640dp x 480dp)
res/layout-large/main_activity.xml # For small tablets (640dp x 480dp and bigger)
res/layout-xlarge/main_activity.xml # For large tablets (960dp x 720dp and bigger)
使用布局别名
如果同时支持低于和高于 Android 3.2 的版本,那么您必须同时对布局使用最小宽度限定符和 large 限定符。因此,您应创建一个名为 res/layout-large/main.xml
的文件,该文件可能与 res/layout-sw600dp/main.xml
完全相同。
为避免同一文件出现这种重复,您可以使用别名文件。例如,您可以定义以下布局:
res/layout/main.xml # single-pane layout
res/layout/main_twopanes.xml # two-pane layout
并添加以下两个文件:
res/values-large/layout.xml
:
- @layout/main_twopanes
res/values-sw600dp/layout.xml
:
- @layout/main_twopanes
这两个文件的内容完全相同,但它们实际上并未定义布局,而只是将 main
设为 main_twopanes
的别名。由于这些文件具有 large
和 sw600dp
选择器,因此无论使用的是哪个 Android 版本,它们都适用于大屏幕(低于 3.2 版本的平板电脑和 TV 与 large
匹配,高于 3.2 版本的平板电脑和 TV 与 sw600dp
匹配)。
如果您在改变尺寸的视图中将位图用作背景,您会注意到,当视图根据屏幕尺寸或视图中的内容增大或缩小时,Android 会缩放您的图片。这通常会导致明显的模糊或其他缩放失真。解决方案是使用九宫格位图,这种特殊格式的 PNG 文件会指示哪些区域可以拉伸,哪些区域不可以拉伸。
九宫格位图基本上是一种标准的 PNG 文件,只不过带有额外的 1 像素边框,指示应拉伸哪些像素(并且带有 .9.png
扩展名,而不只是 .png
)。如图 5 中所示,左边缘和上边缘的黑线之间的交点是可以拉伸的位图区域。
或者,您也可以定义内容在视图内应进入的安全区域,方法是以同样的方式在右边缘和下边缘添加线条。
图 5. 九宫格图片
将九宫格作为背景应用于视图时,框架会正确拉伸图片以适应按钮的尺寸。
务必针对各种屏幕尺寸测试您的应用,这样您就可以确保界面正确缩放。如果您无法访问具有各种不同屏幕尺寸的物理设备,那么也可以使用Android 模拟器来模拟任何屏幕尺寸。
如果您希望在物理设备上进行测试,但又不想购买设备,那么可以使用Firebase 测试实验室访问 Google 数据中心的设备。
如果您不想让您的应用以特定的屏幕尺寸运行,您可以设置屏幕尺寸限制,甚至可以根据设备的屏幕配置来限制哪些设备可以安装您的应用。