构建列表详情布局

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

这种模式特别适合 大型集合(例如电子邮件客户端)元素的相关信息 其中包含电子邮件列表和每封电子邮件的详细内容。 列表-详情也可用于不太重要的路径,例如划分应用 转换为类别列表,其中包含每个类别的 “详细信息”窗格

使用 ListDetailPaneScaffold 实现界面模式

ListDetailPaneScaffold 是一个可组合项,可简化 您的应用中的“列表-详情”模式“列表-详情”基架最多可包含 三个窗格:列表窗格、详细信息窗格和一个可选的额外窗格。通过 基架可用于处理屏幕空间的计算。当屏幕足够大时 详情窗格会显示在列表窗格旁边。在小屏幕上 Scaffold 会自动切换为显示列表或 全屏显示详情窗格。

列表页面旁边显示的详情窗格。
图 1. 当有足够大的屏幕尺寸时,细节内容 列表窗格旁边显示。
选择某项内容后,详情窗格会占据整个屏幕。
图 2. 当屏幕尺寸受限时,详情窗格(因为已选择某项) 占据了整个空间。

声明依赖项

ListDetailPaneScaffoldMaterial 3 自适应布局的一部分 库

您的应用必须包含三个相关 Material 3 库的依赖项:

  • 自适应 - 低层级构建块,例如 HingeInfoPosture
  • adaptive-layout - 自适应布局,例如 ListDetailPaneScaffoldSupportingPaneScaffold
    • adaptive-navigation - 用于在和 在窗格之间

将依赖项添加到应用或模块的 build.gradle 文件中:

Kotlin


implementation("androidx.compose.material3.adaptive:adaptive:1.0.0-alpha12")
implementation("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha12")
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha12")

Groovy


implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-alpha12'
implementation 'androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha12'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha12'

基本用法

按如下方式实现 ListDetailPaneScaffold

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

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

  2. 使用以下代码创建一个 ThreePaneScaffoldNavigatorrememberListDetailPaneScaffoldNavigator 并添加 BackHandler这个 导航器用于在列表窗格、详情窗格和其他窗格之间移动。修改者 声明泛型类型时,导航器也会跟踪 基架(即正在显示的 MyItem)。由于此类型 parcelable,因此导航器可以保存并恢复状态, 自动处理配置更改。通过 BackHandler 支持使用系统返回手势或 按钮。返回按钮的预期行为 ListDetailPaneScaffold 取决于窗口大小和当前 Scaffold 值。如果 ListDetailPaneScaffold 可以支持返回 则 canNavigateBack()true,从而启用 BackHandler

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

  3. scaffoldStatenavigator 传递给 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)
            }
        }
    },
)