adaptive
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePrerequisites
前提条件
The app must:
- Use Compose for all screens. If it's still using Fragments or Views, suggest using the XML to Compose skill to migrate those screens.
- Use Jetpack Navigation 3. If it doesn't, suggest the Navigation 3 skill to migrate the app.
应用必须满足以下要求:
- 所有界面都使用Compose。如果仍在使用Fragments或Views,建议使用XML转Compose技能迁移这些界面。
- 使用Jetpack Navigation 3。如果未使用,建议使用Navigation3技能迁移应用。
Workflow to make an app adaptive
打造自适应应用的工作流程
To make an app adaptive, follow these steps or a subset of them adapting to the
task.
- Step 1: Verify current UI
- Step 2: Make the navigation bar adaptive
- Step 3: Add multi-pane layouts
- Step 4: Make vertical lists adaptive by changing the number of columns
- Step 5: Hide app bars when scrolling
要打造自适应应用,请遵循以下步骤或根据任务需求选择部分步骤执行。
- 步骤1:验证当前UI
- 步骤2:实现自适应导航栏
- 步骤3:添加多窗格布局
- 步骤4:通过调整列数实现垂直列表自适应
- 步骤5:滚动时隐藏应用栏
Step 1. Verify current UI
步骤1. 验证当前UI
Ensure that screenshot tests exist to verify the current UI on different form
factors. If they don't exist, add the Compose Preview Screenshot Testing
tool. Use the following annotation to create previews for all the major form
factors. For example:
kotlin
@Preview(name = "Phone", device = Devices.PHONE, showBackground = true)
@Preview(name = "Foldable", device = Devices.FOLDABLE, showBackground = true)
@Preview(name = "Tablet", device = Devices.TABLET, showBackground = true)
@Preview(name = "Desktop", device = Devices.DESKTOP, showBackground = true)
annotation class FormFactorPreviews
@PreviewTest
@FormFactorPreviews
@Composable
fun FeedScreenPreview() {
SnippetsTheme {
Box {
Text("My Screen")
}
}
}确保存在截图测试,用于验证不同形态设备上的当前UI。如果没有,请添加Compose预览截图测试工具。使用以下注解为所有主要形态设备创建预览。例如:
kotlin
@Preview(name = "Phone", device = Devices.PHONE, showBackground = true)
@Preview(name = "Foldable", device = Devices.FOLDABLE, showBackground = true)
@Preview(name = "Tablet", device = Devices.TABLET, showBackground = true)
@Preview(name = "Desktop", device = Devices.DESKTOP, showBackground = true)
annotation class FormFactorPreviews
@PreviewTest
@FormFactorPreviews
@Composable
fun FeedScreenPreview() {
SnippetsTheme {
Box {
Text("My Screen")
}
}
}Step 2. Make the navigation bar adaptive
步骤2. 实现自适应导航栏
Bottom navigation bars are optimized for touch input when the user is holding a
phone in portrait mode. On larger screen hand-held devices, like tablets and
unfolded foldables, the navigation area must be accessible from the edge of the
screen (navigation rail).
If you need to provide more screen real state for the content, hide the
navigation area. Examples of this include:
- Hiding the navigation bar when the user scrolls down and showing it again when the user scrolls up. The assumption is that when the user is scrolling down, they are consuming content but when scrolling up they are trying to navigate away from that content.
- Hiding the navigation area when its content is distracting. For example, in camera previews or when the content is best displayed in full screen (such as a single photo screen).
When the detail screen is displayed full-screen on mobile, full-screen mode must
be deactivated on larger screens.
Steps to migrate:
- Locate the existing navigation bar.
- Convert each item to a .
NavigationSuiteItem - Identify whether the navigation bar's visibility changes. For example, if it is wrapped with an or
AnimatedContentcomposable. If so, follow the guidance in the "Control navigation area visibility".AnimatedVisibility - Replace the container that held the navigation bar (often a ) with
Scaffoldfrom the Material 3 adaptive layouts library.NavigationSuiteScaffold - Supply the navigation items using the parameter of
navigationItems.NavigationSuiteScaffold
底部导航栏针对用户竖屏手持手机的触摸操作进行了优化。在平板、展开的折叠屏等大屏手持设备上,导航区域必须能从屏幕边缘轻松访问(即导航轨)。
如果需要为内容提供更多屏幕空间,可以隐藏导航区域。例如:
- 用户向下滚动时隐藏导航栏,向上滚动时重新显示。假设用户向下滚动时是在浏览内容,向上滚动时则是想要离开当前内容进行导航。
- 当导航栏内容会分散注意力时将其隐藏。例如在相机预览界面,或内容适合全屏显示的场景(如单张照片展示界面)。
在移动设备上详情界面全屏显示时,大屏设备上必须取消全屏模式。
迁移步骤:
- 找到现有的导航栏。
- 将每个项转换为。
NavigationSuiteItem - 判断导航栏的可见性是否会变化。例如是否被或
AnimatedContent组件包裹。如果是,请遵循「控制导航区域可见性」中的指导。AnimatedVisibility - 将承载导航栏的容器(通常是)替换为Material 3自适应布局库中的
Scaffold。NavigationSuiteScaffold - 通过的
NavigationSuiteScaffold参数提供导航项。navigationItems
Step 2.1. Control navigation area visibility
步骤2.1. 控制导航区域可见性
If the navigation bar's visibility changes - it is hidden under certain
scenarios or on certain screens - this behavior must be maintained with the
adaptive navigation area. This is done using 's
parameter.
NavigationSuiteScaffoldstateSteps to migrate:
- Identify the scenarios under which the navigation bar is hidden. This is usually done with a boolean variable for the visibility. It could be named something like or
isNavBarVisible.shouldShowNavBar - Create an instance of using
NavigationSuiteScaffoldStateand pass it torememberNavigationSuiteScaffoldState().NavigationSuiteScaffold - When the navigation area visibility changes, use a to call
LaunchedEffectorshowon thehide.NavigationSuiteScaffoldState
For example:
kotlin
// Pass this variable to any composable that needs to control the navigation area visibility
var isNavBarVisible by remember { mutableStateOf(true) }
val scaffoldVisibilityState = rememberNavigationSuiteScaffoldState()
NavigationSuiteScaffold(
navigationSuiteItems = navItems,
state = scaffoldVisibilityState
) {
// Main content
}
LaunchedEffect(isNavBarVisible){
if (isNavBarVisible) {
scaffoldVisibilityState.show()
} else {
scaffoldVisibilityState.hide()
}
}如果导航栏的可见性会变化——在某些场景或某些界面下会隐藏——这种行为必须在自适应导航区域中保留。这可以通过的参数实现。
NavigationSuiteScaffoldstate迁移步骤:
- 确定导航栏隐藏的场景。通常会用一个布尔变量来控制可见性,变量名可能类似或
isNavBarVisible。shouldShowNavBar - 使用创建
rememberNavigationSuiteScaffoldState()实例,并将其传递给NavigationSuiteScaffoldState。NavigationSuiteScaffold - 当导航区域可见性变化时,使用调用
LaunchedEffect的NavigationSuiteScaffoldState或show方法。hide
示例:
kotlin
// 将此变量传递给任何需要控制导航区域可见性的组件
var isNavBarVisible by remember { mutableStateOf(true) }
val scaffoldVisibilityState = rememberNavigationSuiteScaffoldState()
NavigationSuiteScaffold(
navigationSuiteItems = navItems,
state = scaffoldVisibilityState
) {
// 主内容
}
LaunchedEffect(isNavBarVisible){
if (isNavBarVisible) {
scaffoldVisibilityState.show()
} else {
scaffoldVisibilityState.hide()
}
}Step 3. Add multi-pane layouts using Navigation 3 Scenes
步骤3. 使用Navigation3 Scenes添加多窗格布局
Analyze the codebase looking for related screens - tapping on something in one
screen opens another screen that shows information related to the first. There
are two canonical screen relationships: list-detail and supporting pane.
IMPORTANT: You must use the Navigation 3 approach to implement
multi-pane layouts. Do not use or
.
SceneStrategyListDetailPaneScaffoldSupportingPaneScaffold分析代码库,寻找存在关联的界面——点击某个界面中的内容会打开另一个显示相关信息的界面。典型的界面关系有两种:列表-详情和辅助窗格。
重要提示:必须使用Navigation3的方法实现多窗格布局。请勿使用或。
SceneStrategyListDetailPaneScaffoldSupportingPaneScaffoldStep 3.1. List-detail
步骤3.1. 列表-详情
Identify the list and detail screens
识别列表和详情界面
List-detail layouts display a list of items (this is the list screen) and
clicking on an item opens a new screen that shows more details about that item
(the detail screen).
Typical usage includes productivity apps like email, notes, and messaging.
Unless requested explicitly, avoid this pattern when the detail content requires
substantial screen space (e.g., images or media that benefits from a full-screen
presentation).
列表-详情布局会显示一个项目列表(即列表界面),点击其中一项会打开一个显示该项目更多详情的新界面(即详情界面)。
典型应用场景包括邮件、笔记、消息等生产力类应用。
除非明确要求,否则当详情内容需要大量屏幕空间时(例如图片或媒体内容,全屏展示效果更佳),请避免使用此模式。
Add a Material list-detail SceneStrategy
添加Material列表-详情SceneStrategy
- Add the library
androidx.compose.material3.adaptive:adaptive-navigation3 - Create an using
androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategyrememberListDetailSceneStrategy - Pass the to
ListDetailSceneStrategyusing itsNavDisplayparametersceneStrategies
- 添加库
androidx.compose.material3.adaptive:adaptive-navigation3 - 使用创建
rememberListDetailSceneStrategy实例androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy - 将通过
ListDetailSceneStrategy参数传递给sceneStrategiesNavDisplay
Use metadata to identify the list and detail screens
使用元数据标识列表和详情界面
- Add metadata using or
entry(metadata = ...)to the list entry usingNavEntry(metadata = ...).ListDetailSceneStrategy.listPane(detailPlaceholder = { <placeholder composable> }) - Use the parameter to add a placeholder on the detail screen when no list items are selected.
detailPlaceholder - Add metadata to the detail entry using .
ListDetailSceneStrategy.detailPane()
- 使用或
entry(metadata = ...)为列表条目添加元数据,调用NavEntry(metadata = ...)。ListDetailSceneStrategy.listPane(detailPlaceholder = { <占位符组件> }) - 使用参数,在未选择列表项时为详情界面添加占位符。
detailPlaceholder - 使用为详情条目添加元数据。
ListDetailSceneStrategy.detailPane()
Important considerations
重要注意事项
- When a detail screen displays its content full-screen on mobile (content fills the entire screen, bars or rails are hidden), full-screen mode must be deactivated if it's part of a list-detail layout.
- Detail screens must not show a back arrow when on a list-detail layout.
For a reference implementation, check the Nav3 Material List Detail
recipe.
- 如果详情界面在移动设备上全屏显示(内容填满整个屏幕,隐藏栏或导航轨),当它属于列表-详情布局时必须取消全屏模式。
- 在列表-详情布局中,详情界面不得显示返回箭头。
参考实现可查看Nav3 Material列表-详情示例。
Step 3.2. Supporting pane
步骤3.2. 辅助窗格
Identify supporting pane screens where a main screen displays a single item, and
selecting it opens a "supporting screen" with more details. The supporting
screen complements the main screen and is shown in a supporting pane.
识别辅助窗格界面:主界面显示单个项目,选中后会打开一个展示更多详情的「辅助界面」,该辅助界面作为主界面的补充,显示在辅助窗格中。
Add a Material supporting pane SceneStrategy
SceneStrategy添加Material辅助窗格SceneStrategy
SceneStrategy- If you haven't already, add the library
androidx.compose.material3.adaptive:adaptive-navigation3 - Create an using
androidx.compose.material3.adaptive.navigation3.SupportingPaneSceneStrategyrememberSupportingPaneSceneStrategy - Pass the to
SupportingPaneSceneStrategyusing itsNavDisplayparametersceneStrategies
- 如果尚未添加,引入库
androidx.compose.material3.adaptive:adaptive-navigation3 - 使用创建
rememberSupportingPaneSceneStrategy实例androidx.compose.material3.adaptive.navigation3.SupportingPaneSceneStrategy - 将通过
SupportingPaneSceneStrategy参数传递给sceneStrategiesNavDisplay
Use metadata to identify the main and supporting screens
使用元数据标识主界面和辅助界面
- Add metadata using or
entry(metadata = ...)to the main entry usingNavEntry(metadata = ...)SupportingPaneSceneStrategy.mainPane() - Add metadata to the supporting entry using
SupportingPaneSceneStrategy.supportingPane()
- 使用或
entry(metadata = ...)为主条目添加元数据,调用NavEntry(metadata = ...)SupportingPaneSceneStrategy.mainPane() - 使用为辅助条目添加元数据
SupportingPaneSceneStrategy.supportingPane()
Step 3.3. Run screenshot tests
步骤3.3. 运行截图测试
If you have made changes, record new reference files. Ask the user to visually
verify that the new layouts are correct.
如果进行了修改,请录制新的参考文件。请用户直观验证新布局是否正确。
Step 4. Make vertical lists adaptive by changing the number of columns
步骤4. 通过调整列数实现垂直列表自适应
Step 4.1. Make lazy lists adaptive
步骤4.1. 实现懒加载列表自适应
Look for the following vertical list composables: ,
, .
LazyColumnLazyVerticalGridLazyVerticalStaggeredGridSteps to migrate:
- Choose a suitable minimum width in dp for the column. It should be large enough so that item is clearly visible to the user.
- For : change to a
LazyColumnand follow the instruction belowLazyVerticalGrid - For : change the
LazyVerticalGridparameter to usecolumnsGridCells.Adaptive(<width>.dp) - For : change the
LazyVerticalStaggeredGridparameter to usecolumnsStaggeredGridCells.Adaptive(<width>.dp)
寻找以下垂直列表组件:、、。
LazyColumnLazyVerticalGridLazyVerticalStaggeredGrid迁移步骤:
- 为列选择合适的最小宽度(单位dp)。宽度应足够大,确保项目对用户清晰可见。
- 对于:改为使用
LazyColumn,并遵循以下说明LazyVerticalGrid - 对于:将
LazyVerticalGrid参数改为使用columnsGridCells.Adaptive(<width>.dp) - 对于:将
LazyVerticalStaggeredGrid参数改为使用columnsStaggeredGridCells.Adaptive(<width>.dp)
Step 4.2. Migrate non-lazy lists to Grid
步骤4.2. 将非懒加载列表迁移至Grid
WARNING: Grid is an experimental API available from Compose 1.11.0-beta01.
Confirm with the user that they are happy to use an experimental API in their
codebase.
Look for any that contains multiple items of the same type and replace
it with . Do not replace it with or any other lazy
layout. Do not place inside the existing . Completely replace it.
ColumnGridLazyVerticalGridGridColumnGridGridConfigurationScopeconfigconstraintsGrid- less than 800dp, a 2x4 grid is used
- 800dp or more, a 4x2 grid is used
kotlin
Grid(
config = {
val maxWidthDp = constraints.maxWidth.toDp()
val (cols, rows) = if (maxWidthDp < 800.dp){
2 to 4
} else{
4 to 2
}
val gapSizeDp = 8.dp
val cellSize = ((maxWidthDp - (gapSizeDp * (cols - 1))) / cols).coerceAtLeast(0.dp)
repeat(cols) { column(cellSize) }
repeat(rows) { row(cellSize) }
gap(gapSizeDp)
}
) { /** items **/ }Grid@OptIn(ExperimentalGridApi::class)警告:Grid是实验性API,从Compose 1.11.0-beta01开始可用。请确认用户愿意在其代码库中使用实验性API。
寻找所有包含多个同类型项目的,将其替换为。请勿替换为或其他懒加载布局。请勿将放在现有内部,直接完全替换。
ColumnGridLazyVerticalGridGridColumn通过向的参数传入一个lambda(的扩展函数)来配置它。在lambda内部,提供了网格容器的最小和最大尺寸,可根据可用尺寸更改行列数。例如,以下代码配置,使其在可用宽度为:
GridconfigGridConfigurationScopeconstraintsGrid- 小于800dp时,使用2列4行的网格
- 800dp或更大时,使用4列2行的网格
kotlin
Grid(
config = {
val maxWidthDp = constraints.maxWidth.toDp()
val (cols, rows) = if (maxWidthDp < 800.dp){
2 to 4
} else{
4 to 2
}
val gapSizeDp = 8.dp
val cellSize = ((maxWidthDp - (gapSizeDp * (cols - 1))) / cols).coerceAtLeast(0.dp)
repeat(cols) { column(cellSize) }
repeat(rows) { row(cellSize) }
gap(gapSizeDp)
}
) { /** 项目 **/ }由于是实验性API,请在所有使用它的函数上添加注解。
Grid@OptIn(ExperimentalGridApi::class)Step 5: Hide App Bars when scrolling
步骤5:滚动时隐藏应用栏
In an app with multiple top-level destinations, each screen must manage its own
app bar state independently. There are two main scroll behaviors:
- : Hides on scroll down, stays hidden while you scroll up until you reach the very top (0 offset).
exitUntilCollapsedScrollBehavior - : Hides on scroll down, shows immediately on scroll up.
enterAlwaysScrollBehavior
在包含多个顶级目标页面的应用中,每个界面必须独立管理自己的应用栏状态。主要有两种滚动行为:
- :向下滚动时隐藏,向上滚动时保持隐藏,直到滚动到最顶部(偏移量为0)才显示。
exitUntilCollapsedScrollBehavior - :向下滚动时隐藏,向上滚动时立即显示。
enterAlwaysScrollBehavior
Final step: Build and test
最终步骤:构建并测试
Build the app and run the local tests. If the project has screenshot tests, run
them but DO NOT update the reference images. Prompt the user to do this after
they have viewed the screenshot diffs.
构建应用并运行本地测试。如果项目包含截图测试,请运行测试但不要更新参考图片。提示用户查看截图差异后再进行更新。
Additional documentation for experimental adaptive APIs
实验性自适应API的补充文档
The following APIs are available from Compose 1.11.0-beta01.
以下API从Compose 1.11.0-beta01开始可用。
FlexBox
FlexBox
Check the FlexBox documentation:
- Overview
- Get started - setup
- Set container behavior
- Set item behavior
查看FlexBox文档:
- 概述
- 快速入门 - 设置
- 设置容器行为
- 设置项目行为
MediaQuery
MediaQuery
Check the MediaQuery documentation when you need to query the device's
screen size, pointer precision, keyboard type, whether it has cameras or
microphones, and other device capabilities.
当需要查询设备屏幕尺寸、指针精度、键盘类型、是否配备摄像头或麦克风及其他设备功能时,请查看MediaQuery文档。
Grid
Grid
Check the Grid documentation when you need to display a fixed number of items in
a grid layout:
- Overview
- Get started - setup
- Set container properties
- Set item properties
当需要在网格布局中显示固定数量的项目时,请查看Grid文档:
- 概述
- 快速入门 - 设置
- 设置容器属性
- 设置项目属性