Ein Listen-Detail-Layout erstellen

Das Listendetail ist ein UI-Muster, das aus einem zweispaltigen Layout besteht, in dem in einer Spalte eine Liste von Elementen und in einer anderen Spalte die Details der aus der Liste ausgewählten Elemente angezeigt werden.

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

UI-Muster mit ListDetailPaneScaffold implementieren

ListDetailPaneScaffold ist ein Composeable, das die Implementierung des Listen-Detail-Musters in Ihrer App vereinfacht. Ein Listen-Detail-Scaffold kann aus bis zu drei Bereichen bestehen: einem Listenbereich, einem Detailbereich und einem optionalen zusätzlichen Bereich. Das Scaffold übernimmt die Berechnungen des Bildschirmbereichs. Wenn die Bildschirmgröße ausreichend ist, wird der Detailbereich neben dem Listenbereich angezeigt. Auf kleinen Bildschirmen wechselt das Scaffolding automatisch dazu, entweder die Liste oder den Detailbereich im Vollbildmodus anzuzeigen.

Ein Detailbereich, der neben der Listenseite angezeigt wird.
Abbildung 1: Wenn der Bildschirm groß genug ist, wird der Detailbereich neben dem Listenbereich angezeigt.
Nachdem ein Element ausgewählt wurde, nimmt der Detailbereich den gesamten Bildschirm ein.
Abbildung 2: Wenn die Bildschirmgröße begrenzt ist, nimmt der Detailbereich (da ein Element ausgewählt wurde) den gesamten Bereich ein.

Abhängigkeiten deklarieren

ListDetailPaneScaffold ist Teil der Material 3-Bibliothek für adaptive Layouts.

Fügen Sie der build.gradle-Datei 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'

  • adaptive: Low-Level-Bausteine wie HingeInfo und Posture
  • adaptive-layout: Adaptive Layouts wie ListDetailPaneScaffold und SupportingPaneScaffold
  • adaptive-navigation: Composeables für die Navigation innerhalb und zwischen Bereichen

Grundlegende Verwendung

Implementieren Sie ListDetailPaneScaffold so:

  1. Verwenden Sie eine Klasse, die die auszuwählenden Inhalte darstellt. Diese Klasse sollte Parcelable sein, 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.

    @Parcelize
    class MyItem(val id: Int) : Parcelable

  2. Erstellen Sie eine ThreePaneScaffoldNavigator mit rememberListDetailPaneScaffoldNavigator und fügen Sie eine BackHandler hinzu. Mit diesem Navigationspfad können Sie zwischen der Liste, den Details und den zusätzlichen Bereichen wechseln. Durch die Deklarierung eines generischen Typs überwacht der Navigationspfad auch den Status des Scaffolds, d. h., welche MyItem angezeigt wird. Da dieser Typ teilbar ist, kann der Status vom Navigationssystem gespeichert und wiederhergestellt werden, um Konfigurationsänderungen automatisch zu verarbeiten. Mit BackHandler können Sie über die Systemgeste oder -schaltfläche zurückgehen. Das erwartete Verhalten der Schaltfläche „Zurück“ für eine ListDetailPaneScaffold hängt von der Fenstergröße und dem aktuellen Scaffold-Wert ab. Wenn der ListDetailPaneScaffold den Rückweg zum aktuellen Status unterstützt, ist canNavigateBack() true und die BackHandler wird aktiviert.

    val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()
    
    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

  3. Übergeben Sie die scaffoldState von der navigator an das ListDetailPaneScaffold-Kompositelement.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        // ...
    )

  4. Reichen Sie die Implementierung des Listenbereichs beim ListDetailPaneScaffold ein. Verwenden Sie AnimatedPane, um die Standardfensteranimationen während der Navigation anzuwenden. Gehen Sie dann mit ThreePaneScaffoldNavigator zum Detailbereich ListDetailPaneScaffoldRole.Detail und zeigen Sie den übergebenen Artikel an.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                MyList(
                    onItemClick = { item ->
                        // Navigate to the detail pane with the passed item
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                    }
                )
            }
        },
        // ...
    )

  5. Fügen Sie die Implementierung des Detailbereichs in ListDetailPaneScaffold ein. Wenn die Navigation abgeschlossen ist, enthält currentDestination den Bereich, zu dem Ihre App gewechselt ist, einschließlich der darin angezeigten Inhalte. Die Property content hat denselben Typ wie im ursprünglichen remember-Aufruf angegeben (MyItem in diesem Beispiel). Sie können also auch auf die Property für alle Daten zugreifen, die angezeigt werden sollen.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane {
                navigator.currentDestination?.content?.let {
                    MyDetails(it)
                }
            }
        },
    )

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

val navigator = rememberListDetailPaneScaffoldNavigator<MyItem>()

BackHandler(navigator.canNavigateBack()) {
    navigator.navigateBack()
}

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane {
            MyList(
                onItemClick = { item ->
                    // Navigate to the detail pane with the passed item
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, item)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane {
            // Show the detail pane content if selected item is available
            navigator.currentDestination?.content?.let {
                MyDetails(it)
            }
        }
    },
)