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

清單/詳細資料是一種 UI 模式,由雙窗格版面配置組成,其中一個窗格會顯示項目清單,另一個窗格則會顯示從清單中選取的項目詳細資料。

此模式特別適合用於提供大型集合元素深入資訊的應用程式,例如電子郵件用戶端,其中包含電子郵件清單和每封電子郵件詳細內容。清單詳細資料也可以用於較不重要的路徑,例如將應用程式偏好設定分割為類別清單,並在詳細資料窗格中顯示各個類別的偏好設定。

清單頁面旁邊顯示的詳細資料窗格。
圖 1. 當螢幕大小足夠時,詳細資料窗格會與清單窗格並排顯示。
選取項目後,詳細資料窗格會取代整個畫面。
圖 2. 當螢幕大小受限時,詳細資料窗格 (因為已選取項目) 會佔用整個空間。

使用 NavigableListDetailPaneScaffold 實作清單/詳細資料模式

NavigableListDetailPaneScaffold 是可組合函式,可簡化在 Jetpack Compose 中實作清單詳細資料版面配置。它會包裝 ListDetailPaneScaffold,並新增內建導覽和預測返回動畫。

清單/詳細資料鷹架最多可支援三個窗格:

  1. 清單窗格:顯示項目集合。
  2. 詳細資料窗格:顯示所選項目的詳細資料。
  3. 額外窗格 (選用):視需要提供額外背景資訊。

這個結構體會根據視窗大小進行調整:

  • 在大型視窗中,清單窗格和詳細資料窗格會並排顯示。
  • 在小視窗中,系統一次只會顯示一個窗格,並在使用者瀏覽時切換。

宣告依附元件

NavigableListDetailPaneScaffoldMaterial 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:用於在分頁內和分頁間導覽的可組合項,以及預設支援導覽的自動調整式版面配置,例如 NavigableListDetailPaneScaffoldNavigableSupportingPaneScaffold

請確認專案包含 compose-material3-adaptive 1.1.0-beta1 以上版本

選擇啟用預測返回手勢

如要在 Android 15 以下版本中啟用預測返回動畫,您必須選擇啟用預測返回手勢。如要選擇採用,請將 android:enableOnBackInvokedCallback="true" 新增至 <application> 標記或 AndroidManifest.xml 檔案中的個別 <activity> 標記。詳情請參閱「選擇啟用預測返回手勢」。

應用程式指定 Android 16 (API 級別 36) 以上版本後,預測返回功能會預設為啟用。

基本用法

請依照下列方式實作 NavigableListDetailPaneScaffold

  1. 使用代表所選內容的類別。使用 Parcelable 類別,支援儲存及還原所選清單項目。使用 kotlin-parcelize 外掛程式為您產生程式碼。
  2. 使用 rememberListDetailPaneScaffoldNavigator 建立 ThreePaneScaffoldNavigator

這個導覽器可用於在清單、詳細資料和其他窗格之間移動。透過宣告泛型類型,導覽器也會追蹤結構體的狀態 (也就是顯示哪個 MyItem)。由於此類型可分割,因此導覽器可以儲存及還原狀態,以便自動處理設定變更。

  1. 將導覽器傳遞至 NavigableListDetailPaneScaffold 可組合函式。

  2. 將清單窗格實作項目提供給 NavigableListDetailPaneScaffold。使用 AnimatedPane 在導覽期間套用預設的窗格動畫。接著,使用 ThreePaneScaffoldNavigator 前往詳細資料窗格 ListDetailPaneScaffoldRole.Detail,並顯示傳入的項目。

  3. NavigableListDetailPaneScaffold 中加入詳細窗格實作項目。

導覽完成後,currentDestination 會包含應用程式已導覽至的窗格,包括窗格中顯示的內容。contentKey 屬性與原始呼叫中指定的類型相同,因此您可以存取需要顯示的任何資料。

  1. 您也可以選擇變更 NavigableListDetailPaneScaffold 中的 defaultBackBehavior。根據預設,NavigableListDetailPaneScaffold 會使用 PopUntilScaffoldValueChangedefaultBackBehavior

如果應用程式需要不同的返回導覽模式,您可以指定其他 BackNavigationBehavior 選項來覆寫此行為。

BackNavigationBehavior 種付款方式

以下範例使用電子郵件應用程式,其中一個窗格顯示電子郵件清單,另一個窗格則顯示詳細檢視畫面。

這項行為著重於整體版面配置結構的變更。在多窗格設定中,變更詳細資料窗格中的電子郵件內容不會影響底層版面配置結構。因此,返回按鈕可能會讓您離開應用程式或目前的導覽圖,因為在目前的內容中,沒有可還原的版面配置變更。在單一窗格版面配置中,按下返回鍵會略過詳細資料檢視畫面中的內容變更,並返回清單檢視畫面,因為這代表明確的版面配置變更。

請見以下範例:

  • 多窗格:您在詳細資料窗格中查看電子郵件 (項目 1)。點選另一封電子郵件 (項目 2) 會更新詳細資料窗格,但清單和詳細資料窗格仍會顯示。按下返回鍵可能會退出應用程式或目前的導覽流程。
  • 單一窗格:您查看項目 1,然後查看項目 2,按下返回鍵會直接返回電子郵件清單窗格。

如要讓使用者在執行每個返回動作時,看到不同的版面配置轉場效果,請使用此選項。

導覽值變更。
PopUntilContentChange

此行為會優先顯示內容。無論版面配置為何,如果您先查看項目 1,再查看項目 2,按下返回鍵都會返回項目 1。

請見以下範例:

  • 多窗格:您在詳細資料窗格中查看項目 1,然後點選清單中的項目 2。詳細資料窗格會更新。按下返回鍵會將詳細資料窗格還原為項目 1。
  • 單一窗格:會發生相同的內容還原情形。

如果使用者預期可透過返回動作返回先前查看的內容,請使用此選項。

兩個詳細資料窗格之間的轉場效果
PopUntilCurrentDestinationChange

這項行為會彈出返回堆疊,直到目前的導覽目的地變更為止。這項原則同樣適用於單一和多窗格版面配置。

請見以下範例:

無論您使用單一窗格或多窗格版面配置,按下返回鍵一律會將焦點從醒目顯示的導覽元素移至先前的目的地。在電子郵件應用程式中,這表示所選窗格中的視覺指示會移位。

當您認為維持目前導覽功能的視覺指示對使用者體驗至關重要時,請使用此選項。

在詳細資料窗格和清單窗格之間切換
PopLatest

這個選項只會從返回堆疊中移除最近的目的地。使用這個選項可返回導覽,且不會略過中間狀態。

完成這些步驟後,程式碼應如下所示:

val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
val scope = rememberCoroutineScope()

NavigableListDetailPaneScaffold(
    navigator = scaffoldNavigator,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    scope.launch {
                        scaffoldNavigator.navigateTo(
                            ListDetailPaneScaffoldRole.Detail,
                            item
                        )
                    }
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            scaffoldNavigator.currentDestination?.contentKey?.let {
                MyDetails(it)
            }
        }
    },
)