建構輔助窗格版面配置

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

詳情請參閱 Material 3 支援窗格指南

使用 Scaffold 實作輔助窗格

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" 新增至 AndroidManifest.xml 檔案中的 <application> 標記或個別 <activity> 標記。

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

建立導覽器

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

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

將導覽器傳遞至 Scaffold

Scaffold 需要 ThreePaneScaffoldNavigator,這是代表 Scaffold 狀態的介面、ThreePaneScaffoldValuePaneScaffoldDirective

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

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

以下是 Scaffold 的完整實作:

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 的使用方式 (請比較下列內容與上一節中完整實作的 Scaffold):

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() },
)

如要進一步控管 Scaffold 的特定層面,請考慮使用 SupportingPaneScaffold,而非 NavigableSupportingPaneScaffold。這個函式會分別接受 PaneScaffoldDirectiveThreePaneScaffoldValue,或 ThreePaneScaffoldState。這項彈性功能可讓您實作窗格間距的自訂邏輯,並決定要同時顯示多少窗格。您也可以新增 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() },
)