构建列表详情布局

“列表-详情”是一种界面模式,由双窗格布局组成,其中一个窗格用于显示项列表,另一个窗格用于显示从列表中选择的项的详细信息。

此模式对于提供有关大型集合元素的深入信息的应用特别有用,例如,包含电子邮件列表和每封电子邮件详细内容的电子邮件客户端。“列表-详情”也可用于不太重要的路径,例如将应用偏好设置划分为一系列类别,并在详情窗格中为每个类别设定偏好设置。

使用 ListDetailPaneScaffold 实现界面模式

ListDetailPaneScaffold 是一个可组合项,用于简化应用中的“列表-详情”模式实现。“列表-详情”基架最多可由三个窗格组成:列表窗格、详情窗格和可选的额外窗格。该框架会处理屏幕空间计算。当有充足的屏幕尺寸时,详情窗格会显示在列表窗格旁边。在小尺寸屏幕上,基架会自动切换为以全屏模式显示列表窗格或详情窗格。

与列表页一起显示的详情窗格。
图 1. 当有足够大的屏幕尺寸时,详情窗格会显示在列表窗格旁边。
选择某项内容后,详情窗格会占据整个屏幕。
图 2. 当屏幕尺寸有限时,详情窗格(由于已选择某项内容)会占据整个空间。

声明依赖项

ListDetailPaneScaffoldMaterial 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 - ListDetailPaneScaffoldSupportingPaneScaffold 等自适应布局
  • adaptive-navigation - 用于在窗格内和窗格之间导航的可组合项

基本用法

按如下方式实现 ListDetailPaneScaffold

  1. 使用表示要选择的内容的类。此类应为 Parcelable,以支持保存和恢复所选列表项。使用 kotlin-parcelize 插件为您生成代码。

    @Parcelize
    class MyItem(val id: Int) : Parcelable

  2. 使用 rememberListDetailPaneScaffoldNavigator 创建 ThreePaneScaffoldNavigator 并添加 BackHandler此导航器用于在列表、详情和额外窗格之间移动。通过声明通用类型,导航器还会跟踪框架的状态(即正在显示哪个 MyItem)。由于此类型是可分块的,因此导航器可以保存和恢复状态,以自动处理配置更改。BackHandler 支持使用系统返回手势或按钮返回。ListDetailPaneScaffold 的返回按钮的预期行为取决于窗口大小和当前的 Scaffold 值。如果 ListDetailPaneScaffold 支持使用当前状态返回,则 canNavigateBack()true,从而启用 BackHandler

    val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. navigator 中的 scaffoldState 传递给 ListDetailPaneScaffold 可组合项。

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

  4. ListDetailPaneScaffold 提供列表窗格实现。 使用 AnimatedPane 在导航期间应用默认窗格动画。然后,使用 ThreePaneScaffoldNavigator 导航到详情窗格 ListDetailPaneScaffoldRole.Detail,并显示传递的项。

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                MyList(
                    onItemClick = { item ->
                        // Navigate to the detail pane with the passed item
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                    }
                )
            }
        },
        // ...
    )

  5. ListDetailPaneScaffold 中添加详情窗格实现。 导航完成后,currentDestination 将包含应用导航到的窗格,包括窗格中显示的内容。content 属性与原始 remember 调用中指定的类型相同(在此示例中为 MyItem),因此您还可以访问该属性以获取您需要显示的任何数据。

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane {
                navigator.currentDestination?.content?.let {
                    MyDetails(it)
                }
            }
        },
    )

实现上述步骤后,您的代码应如下所示:

val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()

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

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            navigator.currentDestination?.content?.let {
                MyDetails(it)
            }
        }
    },
)