构建支持窗格布局

支持面板规范布局将用户注意力集中在您应用程序的主要内容上,同时还显示相关的辅助内容。 例如,主内容面板可能显示有关最近电影的信息,而支持面板显示具有相似主题或相同导演或主演演员的其他电影列表。 有关支持面板规范布局的更多信息,请参阅 Material 3 支持面板指南

实现支持面板

SupportingPaneScaffold 由最多三个面板组成:主面板、支持面板和可选的额外面板。 该脚手架处理将窗口空间分配给这三个面板的所有计算。 在大屏幕上,该脚手架显示主面板,支持面板位于侧面。 在小屏幕上,该脚手架显示主面板或支持面板的全屏显示。

Main content occupying most of the display with supporting content alongside.
图 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'

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

创建导航器和脚手架

在小窗口中,一次只显示一个面板,因此使用 ThreePaneScaffoldNavigator 在面板之间移动。 使用 rememberSupportingPaneScaffoldNavigator 创建导航器的实例。 要处理后退手势,请使用 BackHandler,它检查 canNavigateBack() 并调用 navigateBack()

val navigator = rememberSupportingPaneScaffoldNavigator()

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

该脚手架需要一个 PaneScaffoldDirective,它控制如何分割屏幕以及使用多少间距,以及一个 ThreePaneScaffoldValue,它提供面板的当前状态(例如它们是否已展开或隐藏)。 对于默认行为,请分别使用导航器的 scaffoldDirectivescaffoldValue

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

主面板和支持面板是包含您内容的可组合项。 使用 AnimatedPane 在导航期间应用默认的面板动画。 使用脚手架值检查支持面板是否已隐藏; 如果是这样,则显示一个按钮,该按钮调用 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() },
)