构建列表详情布局

列表-详情是一种UI模式,它由一个双面板布局组成,其中一个面板显示项目列表,另一个面板显示从列表中选择的项目的详细信息。

这种模式对于提供大量集合元素的详细信息的应用程序特别有用,例如,一个电子邮件客户端,它有一个电子邮件列表和每封电子邮件的详细内容。列表-详情也可以用于不太重要的路径,例如将应用程序首选项分为类别列表,每个类别的首选项都在详细信息面板中。

使用ListDetailPaneScaffold实现UI模式

ListDetailPaneScaffold是一个可组合的组件,它简化了在您的应用中实现列表-详情模式的过程。列表-详情脚手架最多可以包含三个面板:列表面板、详情面板和一个可选的额外面板。脚手架处理屏幕空间计算。当有足够的屏幕空间时,详情面板会与列表面板一起显示。在小屏幕尺寸上,脚手架会自动切换为全屏显示列表或详情面板。

A detail pane shown alongside the list page.
图1. 当有足够的屏幕空间时,详情面板会与列表面板一起显示。
After an item is selected, the detail pane takes over the whole screen.
图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
  • 自适应布局 — 自适应布局,例如ListDetailPaneScaffoldSupportingPaneScaffold
  • 自适应导航 — 用于在面板内和面板之间导航的可组合组件

基本用法

按如下方式实现ListDetailPaneScaffold

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

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

  2. 使用rememberListDetailPaneScaffoldNavigator创建一个ThreePaneScaffoldNavigator并添加一个BackHandler此导航器用于在列表、详情和额外面板之间移动。通过声明泛型类型,导航器还跟踪脚手架的状态(即,正在显示哪个MyItem)。由于此类型是可打包的,因此导航器可以保存和恢复状态以自动处理配置更改。BackHandler提供对使用系统后退手势或按钮进行后退导航的支持。ListDetailPaneScaffold的后退按钮的预期行为取决于窗口大小和当前脚手架值。如果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)
            }
        }
    },
)