构建辅助窗格布局

辅助窗格规范布局可将用户的注意力集中在应用的主要内容上,同时显示相关的辅助内容。例如,主内容窗格可能会显示某部新电影的相关信息,而辅助窗格则会显示主题相似或具有相同导演或主演演员的其他电影的列表。如需详细了解辅助窗格规范布局,请参阅 Material 3 辅助窗格指南

实现辅助窗格

SupportingPaneScaffold 最多由三个窗格组成:一个主要窗格、一个辅助窗格和一个可选的额外窗格。基架会处理将窗口空间分配给三个窗格的所有计算。在大屏幕上,该架构会显示主窗格,并在侧边显示辅助窗格。在小屏幕上,框架会全屏显示主窗格或辅助窗格。

主要内容占据大部分显示屏,旁边则是辅助内容。
图 1. 辅助窗格布局。

添加依赖项

SupportingPaneScaffoldMaterial 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'

  • 自适应 - 低级构建块,例如 HingeInfoPosture
  • adaptive-layout - 自适应布局,例如 SupportingPaneScaffold
  • adaptive-navigation - 用于在窗格内和窗格之间导航的可组合项

创建导航器和基架

在小窗口中,一次只能显示一个窗格,因此请使用 ThreePaneScaffoldNavigator 在窗格之间移动。使用 rememberSupportingPaneScaffoldNavigator 创建导航器的实例。如需处理返回手势,请使用可检查 canNavigateBack() 并调用 navigateBack()BackHandler

val navigator = rememberSupportingPaneScaffoldNavigator()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

该框架需要 PaneScaffoldDirective,用于控制如何拆分屏幕以及要使用的间距,还需要 ThreePaneScaffoldValue,用于提供窗格的当前状态(例如它们是展开还是隐藏的)。对于默认行为,请分别使用导航器的 scaffoldDirectivescaffoldValue

SupportingPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    mainPane = { /*...*/ },
    supportingPane = { /*...*/ },
)

主窗格和辅助窗格是包含内容的可组合项。使用 AnimatedPane 在导航期间应用默认窗格动画。使用 Scaffold 值检查辅助窗格是否处于隐藏状态;如果处于隐藏状态,则显示一个按钮,用于调用 navigateTo(ThreePaneScaffoldRole.Secondary) 以显示辅助窗格。

以下是基架的完整实现:

val navigator = rememberSupportingPaneScaffoldNavigator()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

SupportingPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    mainPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            // Main pane content
            if (navigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
                Button(
                    modifier = Modifier.wrapContentSize(),
                    onClick = {
                        navigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
                    }
                ) {
                    Text("Show supporting pane")
                }
            } else {
                Text("Supporting pane is shown")
            }
        }
    },
    supportingPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            // Supporting pane content
            Text("Supporting pane")
        }
    },
)

提取窗格可组合项

SupportingPaneScaffold 的各个窗格提取到各自的可组合项中,以使其可重复使用和测试。如果您想要使用默认动画,请使用 ThreePaneScaffoldScope 访问 AnimatedPane

@Composable
fun ThreePaneScaffoldScope.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")
        }
    }
}

@Composable
fun ThreePaneScaffoldScope.SupportingPane(
    modifier: Modifier = Modifier,
) {
    AnimatedPane(modifier = modifier.safeContentPadding()) {
        // Supporting pane content
        Text("This is the supporting pane")
    }
}

将窗格提取为可组合项可简化 SupportingPaneScaffold 的使用(将以下内容与上一部分中的架构的完整实现进行比较):

val navigator = rememberSupportingPaneScaffoldNavigator()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

SupportingPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = navigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = { navigator.navigateTo(ThreePaneScaffoldRole.Secondary) }
        )
    },
    supportingPane = { SupportingPane() },
)