辅助窗格布局可让用户专注于应用的主要内容,同时显示相关的辅助信息。例如,主窗格可能会显示电影的详细信息,而辅助窗格则会列出类似电影、同一导演执导的电影或由同一演员主演的作品。
如需了解详情,请参阅 Material 3 辅助窗格指南。
实现具有基架的辅助窗格
NavigableSupportingPaneScaffold 是一个可组合项,可简化在 Jetpack Compose 中实现辅助窗格布局的过程。它封装了 SupportingPaneScaffold,并添加了内置导航和预测性返回处理。
辅助窗格基架最多支持三个窗格:
- 主窗格:显示主要内容。
- 辅助窗格:提供与主窗格相关的其他上下文或工具。
- 额外窗格(可选):在需要时用于显示补充内容。
该脚手架会根据窗口大小进行自适应:
- 在较大窗口中,主要窗格和辅助窗格并排显示。
在小窗口中,一次只能看到一个窗格,并随着用户浏览而切换。
图 1. 辅助窗格布局。
添加依赖项
NavigableSupportingPaneScaffold 是 Material 3 自适应布局库的一部分。
将以下三个相关依赖项添加到应用或模块的 build.gradle 文件中:
Kotlin
implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")
Groovy
implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
adaptive-layout:自适应布局,例如
ListDetailPaneScaffold和SupportingPaneScaffoldadaptive-navigation:用于在窗格内和窗格之间导航的可组合项,以及默认支持导航的自适应布局,例如
NavigableListDetailPaneScaffold和NavigableSupportingPaneScaffold
验证您的项目是否包含 compose-material3-adaptive 1.1.0-beta1 或更高版本。
选择启用预测性返回手势
如需在 Android 15 或更低版本中启用预测性返回动画,您必须选择启用对预测性返回手势的支持。如需选择启用,请将 android:enableOnBackInvokedCallback="true" 添加到 AndroidManifest.xml 文件中的 <application> 标记或各个 <activity> 标记。
当应用以 Android 16(API 级别 36)或更高版本为目标平台时,系统会默认启用预测性返回手势。
创建导航器
在小窗口中,一次只能显示一个窗格,因此请使用 ThreePaneScaffoldNavigator 在窗格之间切换。使用 rememberSupportingPaneScaffoldNavigator 创建导航器的实例。
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope()
将导航器传递给 Scaffold
Scaffold 需要 ThreePaneScaffoldNavigator(表示 Scaffold 状态的接口)、ThreePaneScaffoldValue 和 PaneScaffoldDirective。
NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { /*...*/ }, supportingPane = { /*...*/ }, )
主窗格和辅助窗格是包含内容的可组合项。使用 AnimatedPane 在导航期间应用默认窗格动画。使用 scaffold 值检查辅助窗格是否处于隐藏状态;如果是,则显示一个按钮,用于调用 navigateTo(SupportingPaneScaffoldRole.Supporting) 以显示辅助窗格。
对于大屏设备,请使用 ThreePaneScaffoldNavigator.navigateBack() 方法关闭辅助窗格,并传入 BackNavigationBehavior.PopUntilScaffoldValueChange 常量。调用此方法会强制重组 NavigableSupportingPaneScaffold。在重组期间,检查 ThreePaneScaffoldNavigator.currentDestination 属性以确定是否显示辅助窗格。
以下是 Scaffold 的完整实现:
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() val backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { AnimatedPane( modifier = Modifier .safeContentPadding() .background(Color.Red) ) { if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) { Button( modifier = Modifier .wrapContentSize(), onClick = { scope.launch { scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting) } } ) { Text("Show supporting pane") } } else { Text("Supporting pane is shown") } } }, supportingPane = { AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = Modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } } )
提取窗格可组合项
将 SupportingPaneScaffold 的各个窗格提取到各自的可组合项中,使其可重复使用和测试。如果您希望使用默认动画,请使用 ThreePaneScaffoldScope 访问 AnimatedPane:
@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun ThreePaneScaffoldPaneScope.MainPane( shouldShowSupportingPaneButton: Boolean, onNavigateToSupportingPane: () -> Unit, modifier: Modifier = Modifier, ) { AnimatedPane( modifier = modifier.safeContentPadding() ) { // Main pane content if (shouldShowSupportingPaneButton) { Button(onClick = onNavigateToSupportingPane) { Text("Show supporting pane") } } else { Text("Supporting pane is shown") } } } @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun ThreePaneScaffoldPaneScope.SupportingPane( scaffoldNavigator: ThreePaneScaffoldNavigator<Any>, modifier: Modifier = Modifier, backNavigationBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange, ) { val scope = rememberCoroutineScope() AnimatedPane(modifier = Modifier.safeContentPadding()) { Column { // Allow users to dismiss the supporting pane. Use back navigation to // hide an expanded supporting pane. if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) { // Material design principles promote the usage of a right-aligned // close (X) button. IconButton( modifier = modifier.align(Alignment.End).padding(16.dp), onClick = { scope.launch { scaffoldNavigator.navigateBack(backNavigationBehavior) } } ) { Icon(Icons.Default.Close, contentDescription = "Close") } } Text("Supporting pane") } } }
将这些窗格提取到可组合项中可简化 SupportingPaneScaffold 的使用(将以下内容与上一部分中 Scaffold 的完整实现进行比较):
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() NavigableSupportingPaneScaffold( navigator = scaffoldNavigator, mainPane = { MainPane( shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden, onNavigateToSupportingPane = { scope.launch { scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary) } } ) }, supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) }, )
如果您需要更好地控制 Scaffold 的特定方面,请考虑使用 SupportingPaneScaffold,而不是 NavigableSupportingPaneScaffold。此方法分别接受 PaneScaffoldDirective 和 ThreePaneScaffoldValue 或 ThreePaneScaffoldState。这种灵活性使您可以为窗格间距实现自定义逻辑,并确定应同时显示多少个窗格。您还可以通过添加 ThreePaneScaffoldPredictiveBackHandler 来启用预测性返回支持。
添加ThreePaneScaffoldPredictiveBackHandler
附加接受 Scaffold 导航器实例的预测性返回处理程序,并指定 backBehavior。这决定了在返回导航期间如何从返回堆栈中弹出目的地。然后,将 scaffoldDirective 和 scaffoldState 传递给 SupportingPaneScaffold。使用接受 ThreePaneScaffoldState 的重载,传入 scaffoldNavigator.scaffoldState。
在 SupportingPaneScaffold 中定义主窗格和辅助窗格。使用 AnimatedPane 呈现默认窗格动画。
执行完这些步骤后,您的代码应如下所示:
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() val scope = rememberCoroutineScope() ThreePaneScaffoldPredictiveBackHandler( navigator = scaffoldNavigator, backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange ) SupportingPaneScaffold( directive = scaffoldNavigator.scaffoldDirective, scaffoldState = scaffoldNavigator.scaffoldState, mainPane = { MainPane( shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden, onNavigateToSupportingPane = { scope.launch { scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary) } } ) }, supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) }, )