Ein Listen-Detail-Layout erstellen

„List-Detail“ ist ein UI-Muster, das aus einem Zwei-Fenster-Layout besteht, wobei in einem Bereich eine Liste der Elemente und in einem anderen die Details der aus der Liste ausgewählten Elemente angezeigt werden.

Das Muster ist besonders nützlich für Anwendungen, die detaillierte Informationen über Elemente großer Sammlungen bereitstellen, z. B. ein E-Mail-Client, der eine Liste von E-Mails und den detaillierten Inhalt jeder E-Mail-Nachricht enthält. Die Listen-Detailansicht kann auch für weniger kritische Pfade verwendet werden, wie das Aufteilen der App-Einstellungen in eine Liste von Kategorien mit den Einstellungen für jede Kategorie im Detailbereich.

UI-Muster mit ListDetailPaneScaffold implementieren

ListDetailPaneScaffold ist eine zusammensetzbare Funktion, mit der die Implementierung des Listen-Detail-Musters in Ihrer App vereinfacht wird. Ein Listen-Detail-Gerüst kann aus bis zu drei Bereichen bestehen: einem Listenbereich, einem Detailbereich und einem optionalen zusätzlichen Bereich. Das Gerüst übernimmt die Bildschirmfläche. Wenn eine ausreichende Bildschirmgröße verfügbar ist, wird der Detailbereich neben dem Listenbereich angezeigt. Bei kleinen Bildschirmgrößen wird automatisch entweder die Liste oder der Detailbereich im Vollbildmodus angezeigt.

Ein Detailbereich neben der Listenseite.
Abbildung 1: Wenn eine ausreichende Bildschirmgröße verfügbar ist, wird der Detailbereich neben dem Listenbereich angezeigt.
Nachdem ein Element ausgewählt wurde, erstreckt sich der Detailbereich auf den gesamten Bildschirm.
Abbildung 2. Bei einer eingeschränkten Bildschirmgröße nimmt der Detailbereich (seit der Auswahl eines Elements) den gesamten Bereich ein.

Abhängigkeiten deklarieren

ListDetailPaneScaffold ist Teil der adaptiven Bibliothek von Material 3. Fügen Sie in der Datei build.gradle für Ihre App oder Ihr Modul eine Abhängigkeit für die Bibliothek hinzu:

implementation("androidx.compose.material3.adaptive:adaptive:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-layout:1.0.0-alpha07")
implementation("androidx.compose.material3.adaptive:adaptive-navigation:1.0.0-alpha07")

Grundlegende Verwendung

Im Folgenden wird die grundlegende Verwendung von ListDetailPaneScaffold veranschaulicht:

  1. Aktuell ausgewähltes Element aus der Liste in einer änderbaren Statusvariablen speichern: Die Variable enthält das Element, das im Detailbereich angezeigt werden soll. Normalerweise würden Sie das aktuell ausgewählte Element mit null initialisieren. Dies bedeutet, dass noch keine Auswahl getroffen wurde.

    class MyItem(val id: Int) {
        companion object {
            val Saver: Saver<MyItem?, Int> = Saver(
                { it?.id },
                ::MyItem,
            )
        }
    }

    var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
        mutableStateOf(null)
    }

  2. Erstellen Sie die ThreePaneScaffoldNavigator mit rememberListDetailPaneScaffoldNavigator und fügen Sie eine BackHandler hinzu. Dieser Navigator wird verwendet, um zwischen der Liste, den Details und zusätzlichen Bereichen zu wechseln und den Status für das Gerüst anzugeben. Das neue BackHandler bietet Unterstützung, um über die Zurück-Touch-Geste oder die Schaltfläche „Zurück“ des Systems zurückzugehen. 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 das ListDetailPaneScaffold die Zurückkehr zum aktuellen Status unterstützt, ist canNavigateBack() der Wert true und aktiviert BackHandler.

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

  3. Übergeben Sie die scaffoldState aus der navigator, die Sie erstellt haben, an die zusammensetzbare Funktion ListDetailPaneScaffold.

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

  4. Stellen Sie Ihre Listenbereichsimplementierung der ListDetailPaneScaffold bereit. Die Implementierung muss ein Callback-Argument zum Erfassen des neu ausgewählten Elements enthalten. Wenn dieser Callback ausgelöst wird, aktualisieren Sie die Statusvariable selectedItem und verwenden Sie ThreePaneScaffoldNavigator, um den Detailbereich (ListDetailPaneScaffoldRole.Detail) anzuzeigen.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane(Modifier) {
                MyList(
                    onItemClick = { id ->
                        // Set current item
                        selectedItem = id
                        // Switch focus to detail pane
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                    }
                )
            }
        },
        // ...
    )

  5. Fügen Sie die Implementierung des Detailbereichs in ListDetailPaneScaffold ein. Zeigt den Detailinhalt nur an, wenn selectedItem nicht null ist.

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane =
        // ...
        detailPane = {
            AnimatedPane(Modifier) {
                selectedItem?.let { item ->
                    MyDetails(item)
                }
            }
        },
    )

Nach der Implementierung der oben genannten Schritte sollte Ihr Code in etwa so aussehen:

// Currently selected item
var selectedItem: MyItem? by rememberSaveable(stateSaver = MyItem.Saver) {
    mutableStateOf(null)
}

// Create the ListDetailPaneScaffoldState
val navigator = rememberListDetailPaneScaffoldNavigator<Nothing>()

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

ListDetailPaneScaffold(
    directive = navigator.scaffoldDirective,
    value = navigator.scaffoldValue,
    listPane = {
        AnimatedPane(Modifier) {
            MyList(
                onItemClick = { id ->
                    // Set current item
                    selectedItem = id
                    // Display the detail pane
                    navigator.navigateTo(ListDetailPaneScaffoldRole.Detail)
                },
            )
        }
    },
    detailPane = {
        AnimatedPane(Modifier) {
            // Show the detail pane content if selected item is available
            selectedItem?.let { item ->
                MyDetails(item)
            }
        }
    },
)