响应式/自适应布局无论屏幕大小都能提供优化的用户体验。实现响应式/自适应布局,使您的基于视图的应用能够支持所有显示尺寸、方向和配置,包括可调整大小的配置,如 多窗口模式.
响应式设计
支持各种设备外形尺寸的第一步是创建一个对应用可用显示空间变化做出响应的布局。
ConstraintLayout
创建响应式布局的最佳方法是使用 ConstraintLayout
作为 UI 的基本布局。 ConstraintLayout
使您能够根据布局中其他视图的空间关系来指定每个视图的位置和大小。然后,所有视图都可以随着显示空间的变化而一起移动和调整大小。
使用 Android Studio 中的布局编辑器是构建具有 ConstraintLayout
的布局的最简单方法。布局编辑器使您能够将新视图拖动到布局中,应用相对于父级和同级视图的约束,并设置视图属性,所有这些都不需要手动编辑任何 XML。
有关更多信息,请参阅 使用 ConstraintLayout 构建响应式 UI.
响应式宽度和高度
为了确保您的布局能够对不同的显示尺寸做出响应,请使用 wrap_content
、match_parent
或 0dp (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
的宽度和高度如何随着设备方向的变化而改变显示宽度而调整。
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_width
和 layout_weight
属性决定了 SlidingPaneLayout
的行为。在示例中,如果窗口足够大(至少 580dp 宽)可以显示两个视图,则窗格将并排显示。但是,如果窗口宽度小于 580dp,则窗格将彼此滑动以单独占据整个应用程序窗口。
如果窗口宽度大于指定的总最小宽度(580dp),则可以使用 layout_weight
值按比例调整两个窗格的大小。在示例中,列表窗格始终为 280dp 宽,因为它没有权重。但是,详细信息窗格始终填充超过 580dp 的任何水平空间,因为视图的 layout_weight
设置。
备用布局资源
要使您的 UI 设计适应各种显示尺寸,请使用由 资源限定符 标识的备用布局。
您可以通过在应用源代码中创建额外的 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 宽度如何与不同的屏幕尺寸和方向对应起来。
最小宽度限定符的值为 dp,因为重要的是系统考虑像素密度后可用的显示空间量(而不是原始像素分辨率)。
使用资源限定符(如最小宽度)指定的尺寸不是实际屏幕尺寸。相反,尺寸指定应用窗口可用的宽度或高度,以 dp 单位计。Android 系统可能会将部分屏幕用于系统 UI(例如,屏幕底部的系统栏或顶部的状态栏),因此部分屏幕可能无法用于布局。如果您的应用在多窗口模式下使用,则应用只能访问包含应用的窗口的大小。当窗口大小发生改变时,会触发一个带有新窗口大小的配置更改,使系统可以选择合适的布局文件。因此,声明的资源限定符尺寸应仅指定应用所需的 space。系统在提供布局空间时会考虑系统 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 的屏幕。
方向限定符
即使您可能能够仅使用最小宽度和可用宽度限定符的组合来支持所有尺寸变化,您可能还想在用户在纵向和横向方向之间切换时更改用户体验。
为此,您可以将port
或land
限定符添加到布局目录名称中。请确保方向限定符位于大小限定符之后。例如
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
有关所有屏幕配置限定符的更多信息,请参阅应用资源概述。
窗口大小类别
窗口大小类别是视窗断点,可帮助您创建自适应布局。断点识别应用可用的显示区域为紧凑型、中等或扩展。宽度和高度是分别指定的,因此您的应用始终具有宽度和高度的窗口大小类别。
要以编程方式应用自适应布局,请执行以下操作
- 根据窗口大小类别断点创建布局资源
- 使用
WindowSizeClass#compute()
函数(来自Jetpack WindowManager 库)计算应用的宽度和高度窗口大小类别 - 为当前窗口大小类别填充布局资源
有关更多信息,请参阅窗口大小类别。
使用片段的模块化 UI 组件
在为多种显示尺寸设计应用时,请使用片段将 UI 逻辑提取到单独的组件中,以确保您没有在活动之间不必要地重复 UI 行为。然后,您可以将片段组合起来以在大型屏幕上创建多窗格布局,或者将片段放置在小型屏幕上的单独活动中。
例如,列表详细信息模式(参见上面的SlidingPaneLayout)可以使用一个包含列表的片段和另一个包含列表项详细信息的片段来实现。在大型屏幕上,片段可以并排显示;在小型屏幕上,可以单独显示,填充整个屏幕。
要了解更多信息,请参阅片段概述。
活动嵌入
如果您的应用包含多个活动,则活动嵌入使您能够轻松创建自适应 UI。
活动嵌入在应用的任务窗口中同时显示多个活动或同一活动的多个实例。在大型屏幕上,活动可以并排显示;在小型屏幕上,可以叠加显示。
您可以通过创建一个 XML 配置文件来确定应用如何显示其活动,系统使用该文件根据显示大小确定合适的显示方式。或者,您可以进行Jetpack WindowManager API 调用。
活动嵌入支持设备方向更改和可折叠设备,在设备旋转或折叠和展开时堆叠和取消堆叠活动。
有关更多信息,请参阅活动嵌入。
屏幕尺寸和宽高比
在各种屏幕尺寸和宽高比上测试您的应用,以确保您的 UI 正确缩放。
Android 10(API 级别 29)及更高版本支持各种宽高比。可折叠外形尺寸可以从高而窄的屏幕(例如折叠时为 21:9)到展开时为 1:1 的方形宽高比不等。
为了确保与尽可能多的设备兼容,请尽可能多地针对以下屏幕宽高比测试您的应用
如果您没有要测试的所有不同屏幕尺寸的设备,您可以使用Android 模拟器来模拟几乎所有屏幕尺寸。
如果您更愿意在真实设备上进行测试,但没有该设备,您可以使用Firebase 测试实验室访问 Google 数据中心的设备。
其他资源
- Material Design — 了解布局