Android 应用需要支持不断扩大的设备外形规格生态系统。应用的界面应具备自适应能力,能够适应各种屏幕尺寸以及不同的屏幕方向和设备状态。
自适应界面的核心在于灵活性和连续性原则。
灵活性是指布局优化利用可用空间,并在可用空间发生变化时进行调整。调整可以采取多种形式:简单地增加单个视图的大小、重新定位视图以使其位于更易于访问的位置、显示或隐藏其他视图,或这些形式的组合。
连续性是指在从一种窗口大小转换为另一种窗口大小时带来无缝的用户体验。无论用户当前置身于什么体验,都应继续进行而不中断。由于大小的变化可能会伴随着整个视图层次结构的销毁和重新创建,因此务必确保用户不会丢失他们的位置或数据。
需要避免的事项
避免根据物理硬件值来确定布局。您可能会想根据固定值来确定布局,但在许多情况下,这些值对于确定界面可使用的空间没有用处。
在平板电脑上,应用可能会在多窗口模式下运行,这意味着,该应用会与另一个应用共享屏幕。在 Chrome 操作系统中,应用可能会位于可调整大小的窗口中。甚至可能会有多个物理屏幕,例如可折叠设备或配备多个显示屏的设备。在所有这些情况下,物理屏幕尺寸都与决定如何显示内容无关。

出于同样的原因,应避免将应用锁定为特定的屏幕方向或宽高比。虽然设备本身可能会采用特定的屏幕方向,但应用可能仅仅根据其窗口的大小而采用不同的屏幕方向。例如,在平板电脑上,在横屏模式下使用多窗口模式时,应用可能在竖屏模式下,因为它的高度大于宽度。
此外,还应避免尝试确定设备是手机还是平板电脑。具体要符合什么条件才是平板电脑呢?这多少有些主观:是必须有一定的大小,还是宽高比,亦或是大小和宽高比的组合?随着新外形规格的出现,这些假设很容易改变,这种区别就失去了重要性。
应使用断点和窗口大小类,而不采取上述这些做法。
断点和窗口大小类
分配给应用的实际屏幕区域就是应用的窗口。它可能会占据整个屏幕或屏幕的一部分,因此在确定应用的大体布局时应使用窗口大小。
针对多种外形规格进行设计时,应找到确定的这些大体布局在不同方向上分支的阈值。为此,Material Design 准则提供了宽度和高度的断点,可让您将原始大小映射到离散的标准化组,这些组称为窗口大小类。由于垂直滚动的普遍存在,大多数应用主要关注宽度大小类,因此大多数应用可以通过仅处理几个断点来针对所有屏幕尺寸进行优化。(如需详细了解窗口大小类,请参阅支持不同的屏幕尺寸。)
永久性界面元素
Material Design 布局准则定义了应用栏、导航和内容的区域。通常,前两个是位于(或非常接近)视图层次结构根部的永久性界面元素。请注意,“永久性”并不一定意味着视图始终可见,而是指它留在原处,而其他内容视图可能会移动或更改。例如,一个导航元素可能位于当前在屏幕外的滑动抽屉式导航栏中,但抽屉式导航栏始终在那里。
永久性元素可以具备自适应能力,它们通常占据窗口的全宽或全高,因此最好使用大小类来决定要将它们放置在何处。这样就会划定留给内容的空间。在以下代码段中,对于较小的屏幕,activity 使用底部栏;对于较大的屏幕,activity 使用顶部应用栏。如前所述,符合条件的布局使用宽度断点。
<!-- res/layout/main_activity.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- content view(s) -->
<com.google.android.material.bottomappbar.BottomAppBar
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
... />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- res/layout-w600dp/main_activity.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
... />
<!-- content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>
内容
定位永久性界面元素后,使用剩余的空间来显示内容,例如通过将 NavHostFragment
与应用的导航图一起使用。有关其他注意事项,请参阅自适应界面的导航。
确保在不同大小下所有数据都可以呈现
如今的大多数应用框架都利用一个与促成界面的 Android 组件(activity、fragment 和视图)分开的数据模型。对于 Jetpack,此角色通常由 ViewModel 充当,ViewModel 还有一个好处,那就是在配置更改后保持不变(如需了解详情,请参阅 ViewModel 概览)。
当实现适应不同大小的布局时,您可能会想根据当前大小使用不同的数据模型。然而,这违背了单向数据流的原则。数据应向下流动到视图,而诸如用户互动之类的事件应向上流动。如果在另一个方向上创建一种依赖关系,其中数据模型依赖于界面层的配置,会使情况变得非常复杂。当应用改变大小时,您必须考虑从一个数据模型转换为另一个数据模型。
您应让数据模型容纳最大的大小类,然后您可以选择性地显示、隐藏或重新定位界面中的内容,以适应当前的大小类。如果您要决定在大小类之间转换时布局应表现出怎样的行为,可以采用下面这些策略。
扩展内容
规范布局:Feed、Hero
扩展的空间可让您有机会简单地使内容变得更大,并重新设置内容的格式,以使其更易于访问。
使集合变得更大。许多应用在滚动容器(例如 RecyclerView
或 ScrollView
)中显示项目的集合。使容器能够自动变得更大意味着可以显示更多内容。不过,应格外小心,容器中的内容不要过度拉伸或扭曲。例如,对于 RecyclerView
,当宽度不是很小时,不妨考虑使用不同的布局管理器,如 GridLayoutManager
、StaggeredGridLayoutManager
或 FlexboxLayoutManager
。

各个项目也可以利用不同的大小或形状来显示更多内容,并更容易地区分项目边界。
强调主打元素。如果布局具有特定的焦点(如图片或视频),请在应用窗口增大时扩展该焦点,以保持用户的注意力。其他辅助元素可以重新排列在主打视图周围或下面。
构造此类布局的方法有很多,但 ConstraintLayout
特别适合此目的,因为它提供了多种方法来约束子视图的大小(包括按百分比约束或强制采用某个宽高比),以及相对于它自身或其他子级来定位其子级。您可以在使用 ConstraintLayout 构建自适应界面中详细了解所有这些功能。
默认情况下显示可收起的内容。当有可用的空间时,应呈现本来只有通过额外的用户互动(如点按、滚动或手势)才能访问的内容。例如,某些内容在空间较小时出现在标签页式界面中,当有更多可用空间时,可以将这些内容重新排列在列或列表中。
扩大外边距。如果空间太大,即使在利用了所有内容之后,您也无法找到吸引人的合适位置,那么请扩大布局的外边距,以使内容保持居中,并且各个视图具有自然的大小和间距。
或者,全屏组件可以转换为悬浮对话框界面。当该组件需要独占焦点来完成即时用户任务(例如写电子邮件或创建日历活动)时,这特别适合。

添加内容
规范布局:支持面板、列表详情视图
使用辅助面板。辅助面板可以呈现与主要内容相关的额外内容或关联操作,例如文档中的注释或播放列表中的项目。通常,它们将在展开高度下使用屏幕底部的三分之一,或在展开宽度下使用屏幕尾部的三分之一。
一个重要的考虑因素是,当没有足够的空间来显示面板时要将此内容放置在何处。下面是您可以探索的一些备选方案:
- 尾部边缘的侧面抽屉式导航栏(使用
DrawerLayout
) - 底部抽屉式导航栏(使用
BottomSheetBehavior
) - 可通过点按菜单图标访问的菜单或弹出式窗口

创建双窗格布局。大屏幕可能会显示功能的组合,这些功能通常单独出现在较小的屏幕上。许多应用中的常见互动模式是显示项目的列表(如联系人或搜索结果),并在用户选择了某个项目时切换到该项目的详情。应使用列表详情视图在双窗格布局中并排显示这两项功能,而不是针对较大的屏幕放大列表。与辅助面板不同,列表详情视图的详情窗格是一个独立的元素,该元素可以单独显示在较小的屏幕上。
使用 SlidingPaneLayout
专用微件实现列表详情视图。此微件可以根据为两个窗格指定的 layout_width
值自动计算是否有足够的空间来一起显示这两个窗格,任何剩余的空间均可使用 layout_weight
进行分配。如果没有足够的空间,则每个窗格都会使用布局的全宽,详情窗格要么滑出屏幕,要么在列表窗格之上。


创建双窗格布局包含有关如何使用 SlidingPaneLayout
的更多详情。另请注意,此模式可能会影响您构造导航图的方式。如需了解详情,请参阅自适应界面的导航。