使用视图的响应式/自适应设计

尝试 Compose 方式
Jetpack Compose 是 Android 推荐的 UI 工具包。了解如何在 Compose 中使用响应式布局。

响应式/自适应布局提供不受屏幕尺寸限制的优化用户体验。实现响应式/自适应布局可让基于视图的应用支持所有显示尺寸、方向和配置,包括可调整大小的配置(如多窗口模式)。

响应式设计

支持各种设备外形规格的第一步是创建一个布局,使其能响应应用可用显示空间的变化。

ConstraintLayout

创建响应式布局的最佳方法是使用 ConstraintLayout 作为 UI 的基础布局。ConstraintLayout 允许您根据布局中视图之间的空间关系来指定每个视图的位置和大小。然后,所有视图可以随着显示空间的变化一起移动和调整大小。

使用 ConstraintLayout 构建布局最简单的方法是使用 Android Studio 中的布局编辑器。布局编辑器允许您将新视图拖到布局中,相对于父视图和同级视图应用约束,并设置视图属性,所有这些都无需手动编辑任何 XML。

图 3. Android Studio 中的布局编辑器,显示 ConstraintLayout

有关更多信息,请参阅使用 ConstraintLayout 构建响应式界面

响应式宽度和高度

为确保您的布局能够响应不同的显示尺寸,请对视图组件的宽度和高度使用 wrap_contentmatch_parent0dp(匹配约束),而不是硬编码的值

  • wrap_content:视图将其大小设置为适应视图包含的内容。
  • match_parent:视图在父视图中尽可能扩展。
  • 0dp (match constraint):在 ConstraintLayout 中,类似于 match_parent。视图占用其约束范围内的所有可用空间。

例如

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/lorem_ipsum" />

图 4 显示了 TextView 的宽度和高度如何随设备方向变化时显示宽度的变化而调整。

图 4. 响应式 TextView

TextView 将其宽度设置为填充所有可用空间(match_parent),并将其高度设置为刚好等于所包含文本所需的高度(wrap_content),这使得该视图能够适应不同的显示尺寸和不同数量的文本。

如果您使用 LinearLayout,您还可以根据布局权重扩展子视图,以便视图按比例填充可用空间。但是,在嵌套的 LinearLayout 中使用权重需要系统执行多次布局过程来确定每个视图的大小,从而降低 UI 性能。

ConstraintLayout 几乎可以创建所有 LinearLayout 可能实现的布局,而不会带来性能影响,因此将嵌套的 LinearLayout 转换为 ConstraintLayout。然后您就可以使用约束链定义加权布局

自适应设计

您的应用布局应始终对不同的显示尺寸做出响应。但是,即使是响应式布局也无法在所有设备或多窗口模式显示屏上提供最佳用户体验。例如,您为手机设计的界面可能无法在平板电脑上提供最佳用户体验。自适应设计提供了针对不同显示尺寸优化的备用布局。

用于列表-详细信息界面的 SlidingPaneLayout

列表-详细信息界面通常在不同尺寸的屏幕上提供不同的用户体验。在大屏幕上,列表和详细信息窗格通常并排显示。当列表中选择某个项目时,项目信息会显示在详细信息窗格中,而不会更改界面——两个窗格保持并排。然而,在小屏幕上,这两个窗格是分开显示的,每个窗格占用整个显示区域。当列表窗格中的某个项目被选中时,详细信息窗格(包含选中项目的信息)会取代列表窗格。返回导航会将详细信息窗格替换为列表。

SlidingPaneLayout 管理确定哪种用户体验适合当前窗口大小的逻辑

<?xml version="1.0" encoding="utf-8"?>
<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/item_navigation" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>

SlidingPaneLayout 中包含的两个视图的 layout_widthlayout_weight 属性决定了 SlidingPaneLayout 的行为。在本例中,如果窗口足够大(至少 580dp 宽)以显示两个视图,则窗格会并排显示。但是,如果窗口宽度小于 580dp,则窗格会相互滑动以单独占用整个应用窗口。

如果窗口宽度大于指定的总最小宽度 (580dp),则可以使用 layout_weight 值按比例调整两个窗格的大小。在本例中,列表窗格始终为 280dp 宽,因为它没有权重。然而,详细信息窗格由于视图的 layout_weight 设置,始终填充超过 580dp 的任何水平空间。

备用布局资源

为了让您的界面设计适应各种显示尺寸,请使用由资源限定符标识的备用布局。

图 5. 同一个应用为不同显示尺寸使用不同布局。

您可以通过在应用源代码中创建额外的 res/layout/ 目录来提供自适应的、特定于屏幕的布局。为需要不同布局的每种屏幕配置创建一个目录。然后将屏幕配置限定符附加到 layout 目录名称(例如,可用宽度为 600dp 的屏幕使用 layout-w600dp)。

配置限定符表示应用界面可用的可见显示空间。系统在为应用选择布局时会考虑任何系统装饰(例如导航栏)和窗口配置更改(例如多窗口模式)。

要在 Android Studio 中创建备用布局,请参阅使用视图开发界面中的使用布局变体针对不同屏幕进行优化

最小宽度限定符

最小宽度屏幕尺寸限定符允许您为最小宽度以密度无关像素 (dp) 衡量的显示屏提供备用布局。

通过将屏幕尺寸描述为 dp 度量,Android 使您能够创建专为特定显示尺寸设计的布局,而无需考虑不同的像素密度。

例如,您可以创建一个名为 main_activity 的布局,通过在不同目录中创建不同的文件版本来针对手机和平板电脑进行优化

res/layout/main_activity.xml           # For phones (smaller than 600dp smallest width)
res/layout-sw600dp/main_activity.xml   # For 7" tablets (600dp wide or wider)

最小宽度限定符指定显示屏两个边中较小的一个,无论设备当前方向如何,因此这是一种指定布局可用的整体显示尺寸的方法。

以下是其他最小宽度值与典型屏幕尺寸的对应关系

  • 320dp:小尺寸手机屏幕(240x320 ldpi、320x480 mdpi、480x800 hdpi 等)
  • 480dp:大尺寸手机屏幕,约 5 英寸(480x800 mdpi)
  • 600dp:7 英寸平板电脑(600x1024 mdpi)
  • 720dp:10 英寸平板电脑(720x1280 mdpi、800x1280 mdpi 等)

下图更详细地展示了不同的屏幕 dp 宽度如何对应不同的屏幕尺寸和方向。

图 6. 支持不同屏幕尺寸的建议宽度断点。

最小宽度限定符的值是 dp,因为重要的是系统考虑像素密度后可用的显示空间量(而不是原始像素分辨率)。

您使用最小宽度等资源限定符指定的尺寸并非实际屏幕尺寸。确切地说,这些尺寸指定的是您的应用窗口可用的以 dp 为单位的宽度或高度。Android 系统可能会使用一部分屏幕作为系统界面(例如屏幕底部的系统栏或顶部的状态栏),因此部分屏幕可能无法用于您的布局。如果您的应用在多窗口模式下使用,则应用只能访问包含应用的窗口的大小。当窗口大小调整时,会触发包含新窗口大小的配置更改,这使得系统能够选择合适的布局文件。因此,您声明的资源限定符尺寸应仅指定应用所需的空间。系统在为您的布局提供空间时会考虑系统界面占用的任何空间。

可用宽度限定符

除了根据显示屏的最小宽度更改布局之外,您可能还想根据可用的宽度或高度更改布局。例如,您可能希望在屏幕提供至少 600dp 宽度时使用双窗格布局,这可能会因设备是横向还是纵向方向而异。在这种情况下,您应使用可用宽度限定符,如下所示:

res/layout/main_activity.xml         # For phones (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml  # For 7" tablets or any screen with 600dp available width
                                     # (possibly landscape phones)

如果可用高度是您的应用需要考虑的问题,您可以使用可用高度限定符。例如,对于屏幕高度至少为 600dp 的屏幕,使用 layout-h600dp

方向限定符

即使您可以使用最小宽度可用宽度限定符的组合来支持所有尺寸变化,您可能还希望在用户在纵向和横向方向之间切换时改变用户体验。

为此,您可以将 portland 限定符添加到布局目录名称中。请确保方向限定符位于尺寸限定符之后。例如:

res/layout/main_activity.xml                # For phones
res/layout-land/main_activity.xml           # For phones in landscape
res/layout-sw600dp/main_activity.xml        # For 7" tablets
res/layout-sw600dp-land/main_activity.xml   # For 7" tablets in landscape

有关所有屏幕配置限定符的更多信息,请参阅应用资源概览

窗口大小类别

窗口大小类别是视口断点,可帮助您创建自适应布局。这些断点将您的应用可用的显示区域标识为紧凑中等展开。宽度和高度是分开指定的,因此您的应用始终有一个宽度窗口大小类别和一个高度窗口大小类别。

要以编程方式应用自适应布局,请执行以下操作:

  • 根据窗口大小类别断点创建布局资源
  • 使用来自 Jetpack WindowManager 库的 WindowSizeClass#compute() 函数计算应用的宽度和高度窗口大小类别
  • 为当前窗口大小类别膨胀(加载)布局资源

有关更多信息,请参阅窗口大小类别

使用 Fragment 进行模块化 UI 组件设计

在为多种显示尺寸设计应用时,使用 Fragment 将界面逻辑提取到单独的组件中,以确保您不会在多个 Activity 之间不必要地重复界面行为。然后,您可以组合 Fragment 在大屏幕上创建多窗格布局,或者在小屏幕上将 Fragment 放在单独的 Activity 中。

例如,列表-详细信息模式(请参阅上面的SlidingPaneLayout)可以使用一个包含列表的 Fragment 和另一个包含列表项详细信息的 Fragment 实现。在大屏幕上,Fragment 可以并排显示;在小屏幕上,则单独显示,填满屏幕。

如需了解详情,请参阅Fragment 概览。

Activity 嵌入

如果您的应用包含多个 Activity,Activity 嵌入可让您轻松创建自适应界面。

Activity 嵌入可以在应用的任务窗口中同时显示多个 Activity 或同一个 Activity 的多个实例。在大屏幕上,Activity 可以并排显示;在小屏幕上,则层叠显示。

您可以通过创建 XML 配置文件来确定应用如何显示其 Activity,系统会根据显示尺寸使用该文件来确定合适的呈现方式。另外,您也可以进行 Jetpack WindowManager API 调用。

Activity 嵌入支持设备方向变化和可折叠设备,随着设备旋转或折叠和展开,对 Activity 进行堆叠和解除堆叠。

有关更多信息,请参阅Activity 嵌入

屏幕尺寸和宽高比

在各种屏幕尺寸和宽高比的设备上测试您的应用,以确保您的界面能正确缩放。

Android 10(API 级别 29)及更高版本支持多种宽高比。可折叠设备的外形规格各不相同,从折叠时的窄长屏幕(如 21:9)到展开时的方形宽高比(如 1:1)不等。

为了确保与尽可能多的设备兼容,请尽可能针对以下多种屏幕宽高比测试您的应用:

图 7. 各种屏幕宽高比。

如果您无法访问所有不同屏幕尺寸的设备进行测试,可以使用 Android 模拟器来模拟几乎任何屏幕尺寸。

如果您更想在真实设备上测试但没有该设备,可以使用 Firebase Test Lab 来访问 Google 数据中心的设备。

更多资源