Ein Listen-Detail-Layout erstellen

Die Listen-Detailansicht ist ein UI-Muster, das aus einem Splitscreen-Layout besteht. In einem Bereich wird eine Liste von Elementen angezeigt, im anderen die Details der in der Liste ausgewählten Elemente.

Das Muster ist besonders nützlich für Anwendungen, die detaillierte Informationen zu Elementen großer Sammlungen bereitstellen, z. B. ein E-Mail-Client mit einer Liste von E-Mails und dem detaillierten Inhalt jeder E-Mail-Nachricht. Die Listen-Detail-Ansicht kann auch für weniger kritische Pfade verwendet werden, z. B. um App-Einstellungen in eine Liste von Kategorien zu unterteilen, wobei die Einstellungen für jede Kategorie im Detailbereich angezeigt werden.

Ein Detailbereich, der neben der Listenseite angezeigt wird.
Abbildung 1. Wenn genügend Bildschirmgröße verfügbar ist, wird der Detailbereich neben dem Listenbereich angezeigt.
Nachdem ein Element ausgewählt wurde, wird der Detailbereich auf dem gesamten Bildschirm angezeigt.
Abbildung 2. Wenn die Bildschirmgröße begrenzt ist, nimmt der Detailbereich (da ein Element ausgewählt wurde) den gesamten Platz ein.

Listen-Detailansicht-Muster mit NavigableListDetailPaneScaffold implementieren

NavigableListDetailPaneScaffold ist eine zusammensetzbare Funktion, die die Implementierung eines Listen-Detail-Layouts in Jetpack Compose vereinfacht. Sie umschließt ListDetailPaneScaffold und fügt integrierte Navigation und Animationen für die intelligente „Zurück“-Geste hinzu.

Ein Listen-Detailansicht-Scaffold unterstützt bis zu drei Bereiche:

  1. Listenbereich: Hier wird eine Sammlung von Elementen angezeigt.
  2. Detailbereich: Zeigt die Details eines ausgewählten Elements an.
  3. Zusätzlicher Bereich (optional): Hier können bei Bedarf zusätzliche Kontextinformationen angezeigt werden.

Das Scaffold wird an die Fenstergröße angepasst:

  • In großen Fenstern werden die Listen- und Detailbereiche nebeneinander angezeigt.
  • In kleinen Fenstern ist jeweils nur ein Bereich sichtbar. Der Bereich wechselt, wenn Nutzer navigieren.

Abhängigkeiten deklarieren

NavigableListDetailPaneScaffold ist Teil der Material 3-Bibliothek für adaptive Navigation.

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 die Navigation standardmäßig unterstützen, z. B. NavigableListDetailPaneScaffold und NavigableSupportingPaneScaffold

Ihr Projekt muss compose-material3-adaptive Version 1.1.0-beta1 oder höher enthalten.

Intelligente „Zurück“-Touchgeste aktivieren

Wenn Sie die intelligente „Zurück“-Touchgeste in Android 15 oder niedriger aktivieren möchten, müssen Sie die Unterstützung für die intelligente „Zurück“-Touchgeste aktivieren. Wenn Sie die Funktion aktivieren möchten, fügen Sie android:enableOnBackInvokedCallback="true" zum Tag <application> oder zu einzelnen <activity>-Tags in Ihrer AndroidManifest.xml-Datei hinzu. Weitere Informationen finden Sie unter Intelligente „Zurück“-Touchgeste aktivieren.

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

Grundlegende Nutzung

Implementieren Sie NavigableListDetailPaneScaffold so:

  1. Verwenden Sie eine Klasse, die den ausgewählten Inhalt darstellt. Verwenden Sie eine Parcelable-Klasse, um das Speichern und Wiederherstellen des ausgewählten Listenelements zu unterstützen. Verwenden Sie das kotlin-parcelize-Plug-in, um den Code für Sie zu generieren.
  2. Erstellen Sie einen ThreePaneScaffoldNavigator mit rememberListDetailPaneScaffoldNavigator.

Mit diesem Navigator können Sie zwischen der Liste, den Details und den zusätzlichen Bereichen wechseln. Durch die Deklaration eines generischen Typs verfolgt der Navigator auch den Status des Scaffold (also welches MyItem angezeigt wird). Da dieser Typ „parcelable“ ist, kann der Status vom Navigator gespeichert und wiederhergestellt werden, um Konfigurationsänderungen automatisch zu verarbeiten.

  1. Übergeben Sie den Navigator an die NavigableListDetailPaneScaffold-Komposition.

  2. Stellen Sie die Implementierung des Listenbereichs für NavigableListDetailPaneScaffold bereit. Verwenden Sie AnimatedPane, um die Standardanimationen für den Bereich während der Navigation anzuwenden. Verwenden Sie dann ThreePaneScaffoldNavigator, um zum Detailbereich ListDetailPaneScaffoldRole.Detail zu wechseln und das übergebene Element anzuzeigen.

  3. Fügen Sie die Implementierung des Detailbereichs in NavigableListDetailPaneScaffold ein.

Wenn die Navigation abgeschlossen ist, enthält currentDestination den Bereich, zu dem Ihre App navigiert ist, einschließlich der im Bereich angezeigten Inhalte. Das Attribut contentKey hat denselben Typ wie im ursprünglichen Aufruf angegeben, sodass Sie auf alle Daten zugreifen können, die Sie anzeigen möchten.

  1. Ändern Sie optional die defaultBackBehavior in NavigableListDetailPaneScaffold. Standardmäßig verwendet NavigableListDetailPaneScaffold PopUntilScaffoldValueChange für defaultBackBehavior.

Wenn für deine App ein anderes Muster für die Rückwärtsnavigation erforderlich ist, kannst du dieses Verhalten überschreiben, indem du eine andere BackNavigationBehavior-Option angibst.

Optionen für BackNavigationBehavior

Im folgenden Abschnitt wird das Beispiel einer E‑Mail-App mit einer Liste von E‑Mails in einem Bereich und einer Detailansicht im anderen Bereich verwendet.

Dieses Verhalten konzentriert sich auf Änderungen an der allgemeinen Layoutstruktur. In einer Einrichtung mit mehreren Bereichen ändert sich die zugrunde liegende Layoutstruktur nicht, wenn Sie den E‑Mail-Inhalt im Detailbereich ändern. Daher kann es sein, dass die App oder der aktuelle Navigationsgraph über den Button „Zurück“ beendet wird, da es im aktuellen Kontext keine Layoutänderung gibt, zu der zurückgekehrt werden kann. In einem Layout mit einem einzelnen Bereich wird durch Drücken der Zurück-Taste durch die Inhaltsänderungen in der Detailansicht gesprungen und zur Listenansicht zurückgekehrt, da dies eine klare Layoutänderung darstellt.

Betrachten Sie hierzu folgende Beispiele:

  • Mehrere Bereiche:Sie sehen sich eine E‑Mail (Element 1) im Detailbereich an. Wenn Sie auf eine andere E-Mail (Element 2) klicken, wird der Detailbereich aktualisiert, aber die Listen- und Detailbereiche bleiben sichtbar. Wenn Sie die Zurück-Taste drücken, wird möglicherweise die App oder der aktuelle Navigationsvorgang beendet.
  • Einzelner Bereich:Sie sehen sich Element 1 und dann Element 2 an. Wenn Sie auf „Zurück“ klicken, gelangen Sie direkt zum Bereich mit der E‑Mail-Liste.

Verwenden Sie diese Option, wenn Nutzer bei jeder Zurück-Aktion unterschiedliche Layoutübergänge wahrnehmen sollen.

Abbildung, die Änderungen der Navigationswerte in verschiedenen Layoutbereichen zeigt.
PopUntilContentChange

Durch dieses Verhalten werden die angezeigten Inhalte priorisiert. Wenn Sie sich Element 1 und dann Element 2 ansehen, kehren Sie durch Drücken der Zurück-Taste zu Element 1 zurück, unabhängig vom Layout.

Betrachten Sie hierzu folgende Beispiele:

  • Mehrere Bereiche:Sie sehen sich Element 1 im Detailbereich an und klicken dann in der Liste auf Element 2. Der Detailbereich wird aktualisiert. Wenn Sie auf „Zurück“ drücken, wird der Detailbereich wieder auf Element 1 zurückgesetzt.
  • Einzelnes Feld:Der Inhalt wird ebenfalls zurückgesetzt.

Verwenden Sie diese Option, wenn der Nutzer erwartet, dass er mit der Aktion „Zurück“ zum zuvor angesehenen Inhalt zurückkehrt.

den Übergang zwischen zwei Detailbereichen
PopUntilCurrentDestinationChange

Bei diesem Verhalten wird der Back Stack so lange entfernt, bis sich das aktuelle Navigationsziel ändert. Dies gilt sowohl für Layouts mit einem als auch mit mehreren Bereichen.

Betrachten Sie hierzu folgende Beispiele:

Unabhängig davon, ob du ein ein- oder mehrspaltiges Layout verwendest, wird der Fokus durch Drücken der Zurück-Taste immer vom hervorgehobenen Navigationselement zum vorherigen Ziel verschoben. In unserer E-Mail-App bedeutet dies, dass sich die visuelle Anzeige des ausgewählten Bereichs verschiebt.

Verwenden Sie diese Option, wenn eine klare visuelle Anzeige der aktuellen Navigation für die Nutzerfreundlichkeit entscheidend ist.

Zwischen Detail- und Listenbereich wechseln
PopLatest

Mit dieser Option wird nur das letzte Ziel aus dem Backstack entfernt. Verwenden Sie diese Option für die Rückwärtsnavigation, ohne Zwischenzustände zu überspringen.

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

NavigableListDetailPaneScaffold(
    navigator = navigator,
    listPane = {
        AnimatedPane {
            ListContent(
                words = sampleWords,
                selectionState = navigator.currentDestination?.contentKey?.let {
                    SelectionVisibilityState.ShowSelection(it)
                } ?: SelectionVisibilityState.NoSelection,
                onWordClick = { word ->
                    scope.launch {
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, word)
                    }
                },
                animatedVisibilityScope = this@AnimatedPane,
                sharedTransitionScope = this@SharedTransitionLayout
            )
        }
    },
    detailPane = {
        AnimatedPane {
            DetailContent(
                definedWord = navigator.currentDestination?.contentKey,
                animatedVisibilityScope = this@AnimatedPane,
                sharedTransitionScope = this@SharedTransitionLayout,
                onClosePane = {
                    scope.launch {
                        navigator.navigateBack(
                            backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
                        )

                    }
                }
            )
        }
    }