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

响应式/自适应布局无论屏幕大小如何,都能提供优化的用户体验。实现响应式/自适应布局,使您的基于视图的应用能够支持所有显示尺寸、方向和配置,包括可调整大小的配置,例如多窗口模式

响应式设计

支持各种设备外形的第一个步骤是创建一个对应用可用的显示空间变化做出响应的布局。

ConstraintLayout

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

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

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

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

响应式宽度和高度

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

  • 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。然后您可以使用约束链定义加权布局

自适应设计

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

用于列表-详情UI的SlidingPaneLayout

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

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 的任何水平空间。

替代布局资源

要使您的UI设计适应各种显示尺寸,请使用由资源限定符标识的替代布局。

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

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

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

要在Android Studio中创建替代布局,请参阅使用布局变体优化不同屏幕,位于使用视图开发UI中。

最小宽度限定符

最小宽度屏幕尺寸限定符使您可以为以与密度无关的像素(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 系统可能会将部分屏幕用于系统 UI(例如屏幕底部的系统栏或顶部的状态栏),因此部分屏幕可能无法用于您的布局。如果您的应用在多窗口模式下使用,则应用只能访问包含该应用的窗口的大小。当窗口大小调整时,它会触发具有新窗口大小的配置更改,使系统能够选择合适的布局文件。因此,您声明的资源限定符尺寸应仅指定您的应用所需的空間。系统在为您的布局提供空间时会考虑系统 UI 使用的任何空间。

可用宽度限定符

您可能不希望根据显示器的最小宽度来更改布局,而是希望根据可用的宽度或高度来更改布局。例如,当屏幕提供至少 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)

如果可用高度对您的应用很重要,您可以使用可用高度限定符。例如,layout-h600dp 用于屏幕高度至少为 600dp 的屏幕。

方向限定符

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

为此,您可以将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

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

窗口尺寸类别

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

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

有关更多信息,请参阅窗口尺寸类别

使用片段的模块化 UI 组件

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

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

要了解更多信息,请参阅片段概述。

活动嵌入

如果您的应用包含多个活动,则活动嵌入使您可以轻松创建自适应 UI。

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

您可以通过创建系统用来根据显示尺寸确定适当演示的 XML 配置文件来确定应用如何显示其活动。或者,您可以进行Jetpack WindowManager API调用。

活动嵌入支持设备方向更改和可折叠设备,在设备旋转或折叠和展开时堆叠和取消堆叠活动。

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

屏幕尺寸和纵横比

在各种屏幕尺寸和纵横比上测试您的应用,以确保您的 UI 正确缩放。

Android 10(API 级别 29)及更高版本支持各种纵横比。可折叠外形尺寸可以从高而窄的屏幕(例如折叠时的 21:9)到展开时的 1:1 正方形纵横比不等。

为了确保与尽可能多的设备兼容,请尽可能测试您的应用的以下屏幕纵横比:

图 7. 各种屏幕纵横比。

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

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

其他资源