建構清單/詳細資料版面配置

清單/詳細資料是一種 UI 模式,由雙窗格版面配置組成,其中一個窗格 會顯示項目清單,另一個窗格則顯示所選項目的詳細資料 確定要繼續嗎?

這個模式對於 大型集合中的元素相關資訊,例如電子郵件用戶端 當中包含電子郵件清單和每封電子郵件的詳細內容。 較不重要的路徑也可使用清單/詳細資料功能,例如分割應用程式 列出偏好設定,並將 詳細資料窗格

使用 ListDetailPaneScaffold 實作 UI 模式

ListDetailPaneScaffold 這個可組合函式可以簡化 應用程式中的清單/詳細資料模式。清單/詳細資料 Scaffold 最多可包含 三個窗格:清單窗格、詳細資料窗格,以及選用的額外窗格。 scaffold 會處理螢幕空間計算作業。螢幕大小充足時 詳細資料窗格會顯示在清單窗格旁。小螢幕裝置 Scaffold 會自動切換為顯示清單 以全螢幕模式顯示詳細資料窗格。

清單頁面旁顯示的詳細資料窗格。
圖 1. 在螢幕尺寸足夠的情況下顯示細節 窗格就會與清單窗格一起顯示。
選取項目後,詳細資料窗格會佔據整個螢幕。
圖 2. 當螢幕大小限制時,詳細資料窗格 (因為已選取項目) 就會佔據整個空間。

宣告依附元件

ListDetailPaneScaffoldMaterial 3 自動調整式版面配置的一部分 程式庫

應用程式必須包含三個相關 Material 3 程式庫的依附元件:

  • 自動調整 - 低階建構模塊,例如 HingeInfoPosture
  • 自動調整版面配置 - 自動調整式版面配置,例如 ListDetailPaneScaffoldSupportingPaneScaffold
    • 自動調整導覽 - 可供瀏覽和瀏覽的可組合函式 窗格之間的

將依附元件新增至應用程式或模組的 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這個 導覽工具可用於在清單、詳細資料和其他窗格之間移動。變更者: 宣告一般類型時,導覽器也會追蹤 Sscaffold (也就是目前顯示 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)
            }
        }
    },
)