Tworzenie układu ze szczegółami listy

Szczegóły listy to wzorzec interfejsu użytkownika składający się z układu z 2 panelami, w którym jeden panel zawiera listę elementów, a inny zawiera szczegółowe informacje o elementach wybranych z listy.

Wzorzec jest szczególnie przydatny w aplikacjach, które dostarczają szczegółowe informacje o elementach dużych zbiorów, takich jak klient poczty e-mail z listą e-maili i szczegółową treścią każdej wiadomości. Szczegóły listy mogą być też używane w przypadku mniej ważnych ścieżek, na przykład do dzielenia ustawień aplikacji na listę kategorii z preferencjami dla każdej kategorii w panelu szczegółów.

Zaimplementuj wzorzec interfejsu za pomocą funkcji ListDetailPaneScaffold

ListDetailPaneScaffold to funkcja kompozycyjna, która upraszcza implementację wzorca szczegółów listy w aplikacji. Szkielet szczegółów listy może się składać z maksymalnie 3 paneli: panelu listy, panelu szczegółów i opcjonalnego panelu dodatkowego. Platforma obsługuje obliczenia przestrzeni ekranu. Gdy dostępny jest wystarczający rozmiar ekranu, obok panelu z listą wyświetla się panel szczegółów. Na małych ekranach automatycznie przełącza się ono na wyświetlanie listy lub panelu szczegółów na pełnym ekranie.

Okienko szczegółów widoczne obok strony z listą.
Rysunek 1. Gdy dostępna będzie wystarczająca ilość ekranu, panel szczegółów pojawi się obok panelu listy.
Po wybraniu elementu okienko szczegółów zajmuje cały ekran.
Rysunek 2. Przy ograniczonym rozmiarze ekranu okienko szczegółów (ponieważ został wybrany element) zajmuje całą przestrzeń.

Deklarowanie zależności

ListDetailPaneScaffold jest częścią biblioteki adaptacyjnej Material 3. Dodaj zależność dla biblioteki w pliku build.gradle aplikacji lub modułu:

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")

Podstawowe użycie

Poniżej pokazujemy podstawowe użycie ListDetailPaneScaffold:

  1. Zapisać aktualnie wybrany element z listy w zmiennej zmiennej stanu. Zmienna zawiera element, który ma zostać wyświetlony w panelu szczegółów. Zwykle do zainicjowania wybranego obecnie elementu najlepiej nadaje się parametr null, co oznacza, że jeszcze nie wybrano żadnego elementu.

    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. Utwórz ThreePaneScaffoldNavigator z rememberListDetailPaneScaffoldNavigator i dodaj BackHandler. Ten nawigator służy do przechodzenia między panelami z listą, szczegółami i dodatkowymi panelami oraz przekazywaniem stanu do rusztu. Dodany BackHandler zapewnia obsługę przechodzenia do tyłu za pomocą systemowego gestu lub przycisku Wstecz. Oczekiwane działanie przycisku Wstecz w przypadku elementu ListDetailPaneScaffold zależy od rozmiaru okna i bieżącej wartości scaffold. Jeśli ListDetailPaneScaffold obsługuje przywrócenie do obecnego stanu, canNavigateBack() ma wartość true, co powoduje włączenie BackHandler.

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

  3. Przekaż scaffoldState z utworzonego navigator elementu do funkcji kompozycyjnej ListDetailPaneScaffold.

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

  4. Prześlij implementację panelu listy do ListDetailPaneScaffold. Upewnij się, że implementacja zawiera argument wywołania zwrotnego do przechwytywania nowo wybranego elementu. Po wywołaniu tego wywołania zwrotnego zaktualizuj zmienną stanu selectedItem i użyj polecenia ThreePaneScaffoldNavigator, aby wyświetlić panel szczegółów (ListDetailPaneScaffoldRole.Detail).

    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. Umieść implementację panelu szczegółów w ListDetailPaneScaffold. Wyświetl szczegóły tylko wtedy, gdy selectedItem nie ma wartości null.

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

Po wykonaniu powyższych czynności kod powinien wyglądać mniej więcej tak:

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