建構輔助窗格版面配置

輔助窗格版面配置可讓使用者專注於應用程式的主內容,同時顯示相關的輔助資訊。舉例來說,主窗格可能會顯示電影的詳細資料,而輔助窗格則會列出類似的電影、同一位導演的電影,或有相同演員的作品。

詳情請參閱 Material 3 支援窗格規範

使用 NavigableSupportingPaneScaffold 實作輔助窗格

NavigableSupportingPaneScaffold 是可組合函式,可簡化在 Jetpack Compose 中實作支援的窗格版面配置。它會包裝 SupportingPaneScaffold,並新增內建的導覽和預測返回處理功能。

輔助窗格架構最多可支援三個窗格:

  • 主要窗格:顯示主要內容。
  • 輔助窗格:提供與主要窗格相關的額外背景資訊或工具。
  • 額外窗格 (選用):用於顯示必要的額外內容。

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

  • 在大視窗中,主要窗格和輔助窗格會並排顯示。
  • 在小視窗中,系統會一次只顯示一個窗格,並在使用者瀏覽時切換。

    主要內容占據大部分顯示畫面,旁邊則是輔助內容。
    圖 1. 支援窗格版面配置。

新增依附元件

NavigableSupportingPaneScaffoldMaterial 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> [標記或 android:enableOnBackInvokedCallback="true" 新增至 <application> 標記或個別 <activity> 標記,在 AndroidManifest.xml 檔案中。

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

建立導覽器

在小型視窗中,系統一次只會顯示一個窗格,因此請使用 ThreePaneScaffoldNavigator 在窗格之間移動。使用 rememberSupportingPaneScaffoldNavigator 建立導覽器的例項。

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

將導覽器傳遞至鷹架

結構體需要 ThreePaneScaffoldNavigator,這是代表結構體狀態的介面、ThreePaneScaffoldValuePaneScaffoldDirective

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = { /*...*/ },
    supportingPane = { /*...*/ },
)

主要窗格和輔助窗格是包含內容的可組合項。使用 AnimatedPane 在導覽期間套用預設的窗格動畫。使用結構體值檢查是否隱藏支援窗格;如果是的話,請顯示呼叫 navigateTo(SupportingPaneScaffoldRole.Supporting) 的按鈕,以便顯示支援窗格。

以下是結構體的完整實作方式:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        AnimatedPane(
            modifier = Modifier
                .safeContentPadding()
                .background(Color.Red)
        ) {
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
                Button(
                    modifier = Modifier
                        .wrapContentSize(),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
                        }
                    }
                ) {
                    Text("Show supporting pane")
                }
            } else {
                Text("Supporting pane is shown")
            }
        }
    },
    supportingPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            Text("Supporting pane")
        }
    }
)

擷取窗格可組合項

SupportingPaneScaffold 的個別窗格擷取至各自的可組合項,讓這些窗格可重複使用且可測試。如果您想要使用預設動畫,請使用 ThreePaneScaffoldScope 存取 AnimatedPane

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.MainPane(
    shouldShowSupportingPaneButton: Boolean,
    onNavigateToSupportingPane: () -> Unit,
    modifier: Modifier = Modifier,
) {
    AnimatedPane(
        modifier = modifier.safeContentPadding()
    ) {
        // Main pane content
        if (shouldShowSupportingPaneButton) {
            Button(onClick = onNavigateToSupportingPane) {
                Text("Show supporting pane")
            }
        } else {
            Text("Supporting pane is shown")
        }
    }
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.SupportingPane(
    modifier: Modifier = Modifier,
) {
    AnimatedPane(modifier = modifier.safeContentPadding()) {
        // Supporting pane content
        Text("This is the supporting pane")
    }
}

將窗格擷取至可組合項,可簡化 SupportingPaneScaffold 的使用方式 (請將下列內容與上一節中所述的完整架構實作方式進行比較):

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)

如果您需要進一步控管結構的特定層面,請考慮使用 SupportingPaneScaffold 而非 NavigableSupportingPaneScaffold。這個方法會分別接受 PaneScaffoldDirectiveThreePaneScaffoldValueThreePaneScaffoldState。這項彈性功能可讓您為窗格間距實作自訂邏輯,並決定同時顯示多少個窗格。您也可以新增 ThreePaneScaffoldPredictiveBackHandler 來啟用預測返回支援功能。

新增「ThreePaneScaffoldPredictiveBackHandler

請附加預測返回處理常式,該處理常式會採用結構定義導覽器例項,並指定 backBehavior。這會決定在返回導覽期間,如何從返回堆疊彈出目的地。然後將 scaffoldDirectivescaffoldState 傳遞至 SupportingPaneScaffold。使用可接受 ThreePaneScaffoldState 的超載,並傳入 scaffoldNavigator.scaffoldState

SupportingPaneScaffold 中定義主要和輔助窗格。使用 AnimatedPane 的預設窗格動畫。

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

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

ThreePaneScaffoldPredictiveBackHandler(
    navigator = scaffoldNavigator,
    backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
)

SupportingPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    scaffoldState = scaffoldNavigator.scaffoldState,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)