Layout mit unterstützendem Bereich erstellen

Das Layout mit dem unterstützenden Bereich sorgt dafür, dass der Fokus des Nutzers auf den Hauptinhalten der App bleibt, während relevante unterstützende Informationen angezeigt werden. Im Hauptbereich können beispielsweise Details zu einem Film angezeigt werden, während im unterstützenden Bereich ähnliche Filme, Filme desselben Regisseurs oder Werke mit denselben Schauspielern aufgeführt werden.

Weitere Informationen finden Sie in den Richtlinien für unterstützende Bereiche in Material 3.

Unterstützenden Bereich mit einem Scaffold implementieren

NavigableSupportingPaneScaffold ist ein Composable, das die Implementierung eines Layouts mit unterstützendem Bereich in Jetpack Compose vereinfacht. Es umschließt SupportingPaneScaffold und fügt eine integrierte Navigation und die Verarbeitung der intelligenten „Zurück“-Touchgeste hinzu.

Ein unterstützender Bereich kann bis zu drei Bereiche enthalten:

  • Hauptbereich: Hier werden die primären Inhalte angezeigt.
  • Unterstützender Bereich: Hier werden zusätzlicher Kontext oder Tools im Zusammenhang mit dem Hauptbereich bereitgestellt.
  • Zusätzlicher Bereich (optional): Wird bei Bedarf für zusätzliche Inhalte verwendet.

Das Scaffold wird an die Fenstergröße angepasst:

  • In großen Fenstern werden der Hauptbereich und der unterstützende Bereich nebeneinander angezeigt.
  • In kleinen Fenstern ist jeweils nur ein Bereich sichtbar. Die Bereiche werden gewechselt, wenn Nutzer navigieren.

    Der Hauptinhalt nimmt den größten Teil des Displays ein, während der unterstützende Inhalt daneben angezeigt wird.
    Abbildung 1 Layout mit unterstützendem Bereich

Abhängigkeiten hinzufügen

NavigableSupportingPaneScaffold ist Teil der adaptiven Layoutbibliothek von Material 3.

Fügen Sie der Datei build.gradle Ihrer App oder Ihres Moduls die folgenden drei zugehörigen Abhängigkeiten hinzu:

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'
  • adaptiv: Bausteine auf niedriger Ebene wie HingeInfo und Posture

  • adaptive-layout: Adaptive Layouts wie ListDetailPaneScaffold und SupportingPaneScaffold

  • adaptive-navigation: Composables für die Navigation innerhalb und zwischen Bereichen sowie adaptive Layouts, die standardmäßig die Navigation unterstützen, z. B. NavigableListDetailPaneScaffold und NavigableSupportingPaneScaffold

Prüfen Sie, ob Ihr Projekt die Version 1.1.0-beta1 oder höher von compose-material3-adaptive enthält.

Intelligente „Zurück“-Touchgeste aktivieren

Wenn Sie Animationen für die intelligente „Zurück“-Touchgeste in Android 15 oder niedriger aktivieren möchten, müssen Sie die Unterstützung für diese Touchgeste aktivieren. Fügen Sie dazu in der Datei AndroidManifest.xml dem Tag <application> oder einzelnen <activity> Tags android:enableOnBackInvokedCallback="true" hinzu.

Wenn Ihre App auf Android 16 (API‑Level 36) oder höher ausgerichtet ist, ist die intelligente „Zurück“-Touchgeste standardmäßig aktiviert.

Navigator erstellen

In kleinen Fenstern wird jeweils nur ein Bereich angezeigt. Verwenden Sie daher einen ThreePaneScaffoldNavigator, um zwischen den Bereichen zu wechseln. Erstellen Sie eine Instanz des Navigators mit rememberSupportingPaneScaffoldNavigator.

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

Navigator an das Scaffold übergeben

Das Scaffold erfordert einen ThreePaneScaffoldNavigator, eine Schnittstelle , die den Status des Scaffolds, den ThreePaneScaffoldValue und eine PaneScaffoldDirective darstellt.

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

Der Hauptbereich und der unterstützende Bereich sind Composables, die Ihre Inhalte enthalten. Verwenden Sie AnimatedPane, um die Standardanimationen für Bereiche während der Navigation anzuwenden. Verwenden Sie den Scaffold-Wert, um zu prüfen, ob der unterstützende Bereich ausgeblendet ist. Wenn dies der Fall ist, zeigen Sie eine Schaltfläche an, mit der navigateTo(SupportingPaneScaffoldRole.Supporting) aufgerufen wird, um den unterstützenden Bereich anzuzeigen.

Verwenden Sie für große Bildschirme die ThreePaneScaffoldNavigator.navigateBack() Methode, um den unterstützenden Bereich zu schließen. Übergeben Sie dabei die BackNavigationBehavior.PopUntilScaffoldValueChange Konstante. Durch den Aufruf dieser Methode wird eine Neuzusammensetzung von NavigableSupportingPaneScaffold erzwungen. Prüfen Sie während der Neuzusammensetzung die ThreePaneScaffoldNavigator.currentDestination Eigenschaft, um zu ermitteln, ob der unterstützende Bereich angezeigt werden soll.

Hier ist eine vollständige Implementierung des Scaffolds:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()
val backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange

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()) {
            Column {
                // Allow users to dismiss the supporting pane. Use back navigation to
                // hide an expanded supporting pane.
                if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) {
                    // Material design principles promote the usage of a right-aligned
                    // close (X) button.
                    IconButton(
                        modifier =  Modifier.align(Alignment.End).padding(16.dp),
                        onClick = {
                            scope.launch {
                                scaffoldNavigator.navigateBack(backNavigationBehavior)
                            }
                        }
                    ) {
                        Icon(Icons.Default.Close, contentDescription = "Close")
                    }
                }
                Text("Supporting pane")
            }

        }
    }
)

Composable für Bereiche extrahieren

Extrahieren Sie die einzelnen Bereiche eines SupportingPaneScaffold in eigene Composables, damit sie wiederverwendbar und testbar sind. Verwenden Sie ThreePaneScaffoldScope um auf AnimatedPane zuzugreifen, wenn Sie die Standardanimationen verwenden möchten:

@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(
    scaffoldNavigator: ThreePaneScaffoldNavigator<Any>,
    modifier: Modifier = Modifier,
    backNavigationBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange,
) {
    val scope = rememberCoroutineScope()
    AnimatedPane(modifier = Modifier.safeContentPadding()) {
        Column {
            // Allow users to dismiss the supporting pane. Use back navigation to
            // hide an expanded supporting pane.
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) {
                // Material design principles promote the usage of a right-aligned
                // close (X) button.
                IconButton(
                    modifier =  modifier.align(Alignment.End).padding(16.dp),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateBack(backNavigationBehavior)
                        }
                    }
                ) {
                    Icon(Icons.Default.Close, contentDescription = "Close")
                }
            }
            Text("Supporting pane")
        }

    }
}

Durch das Extrahieren der Bereiche in Composables wird die Verwendung von SupportingPaneScaffold vereinfacht. Vergleichen Sie dazu die folgende Implementierung mit der vollständigen Implementierung des Scaffolds im vorherigen Abschnitt:

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

Wenn Sie mehr Kontrolle über bestimmte Aspekte des Scaffolds benötigen, sollten Sie SupportingPaneScaffold anstelle von NavigableSupportingPaneScaffold verwenden. Dabei werden PaneScaffoldDirective und ThreePaneScaffoldValue oder ThreePaneScaffoldState separat akzeptiert. Diese Flexibilität ermöglicht es Ihnen, benutzerdefinierte Logik für den Abstand zwischen den Bereichen zu implementieren und festzulegen, wie viele Bereiche gleichzeitig angezeigt werden sollen. Sie können auch die Unterstützung für die intelligente „Zurück“-Touchgeste aktivieren, indem Sie ThreePaneScaffoldPredictiveBackHandler hinzufügen.

ThreePaneScaffoldPredictiveBackHandler hinzufügen

Fügen Sie den Handler für die intelligente „Zurück“-Touchgeste hinzu, der eine Scaffold-Navigator-Instanz verwendet, und geben Sie das backBehavior an. Dadurch wird festgelegt, wie Ziele während der Rückwärtsnavigation aus dem Backstack entfernt werden. Übergeben Sie dann scaffoldDirective und scaffoldState an SupportingPaneScaffold. Verwenden Sie die Überladung, die ein ThreePaneScaffoldState akzeptiert, und übergeben Sie scaffoldNavigator.scaffoldState.

Definieren Sie den Hauptbereich und den unterstützenden Bereich in SupportingPaneScaffold. Verwenden Sie AnimatedPane für die Standardanimationen für Bereiche.

Nachdem Sie diese Schritte ausgeführt haben, sollte Ihr Code in etwa so aussehen:

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