Tworzenie układu ze szczegółami listy

Lista z szczegółami to wzór interfejsu, który składa się z dwóch paneli. Jeden z nich zawiera listę elementów, a drugi – szczegóły elementów wybranych z listy.

Ten wzór jest szczególnie przydatny w przypadku aplikacji, które dostarczają szczegółowych informacji o elementach dużych kolekcji, np. klienta poczty e-mail z listą e-maili i szczegółowymi treściami każdego e-maila. Lista–szczegóły może być też używana na mniej istotnych ścieżkach, np. do podziału preferencji aplikacji na listę kategorii z preferencjami dla każdej kategorii w panelu szczegółów.

Wdrażanie wzorca interfejsu użytkownika za pomocą ListDetailPaneScaffold

ListDetailPaneScaffold to komponent, który upraszcza implementację w aplikacji wzorca listy i szczegółów. Szkielet listy i szczegółów może składać się z maksymalnie 3 paneli: panelu listy, panelu szczegółów i opcjonalnego dodatkowego panelu. Szkielet obsługuje obliczenia dotyczące miejsca na ekranie. Jeśli rozmiar ekranu jest wystarczający, panel szczegółów jest wyświetlany obok panelu listy. Na małych ekranach automatycznie przełącza się na wyświetlanie listy lub panelu szczegółów na pełnym ekranie.

Panel z informacjami wyświetlany obok strony listy.
Rysunek 1. Jeśli rozmiar ekranu jest wystarczający, panel szczegółowy jest wyświetlany obok panelu listy.
Po wybraniu elementu panel szczegółów zajmuje cały ekran.
Rysunek 2. Gdy rozmiar ekranu jest ograniczony, panel szczegółów (po wybraniu elementu) zajmuje całą przestrzeń.

Deklarowanie zależności

ListDetailPaneScaffold jest częścią biblioteki Material 3 z dopasowywaniem układu.

Dodaj do pliku build.gradle aplikacji lub modułu te 3 powiązane zależności:

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 – elementy składowe niskiego poziomu, takie jak HingeInfo i Posture.
  • adaptive-layout – układy adaptacyjne, takie jak ListDetailPaneScaffold i SupportingPaneScaffold
  • adaptive-navigation – komponenty do nawigacji w obrębie paneli i między nimi

Podstawowe użycie

Zaimplementuj dyrektywę ListDetailPaneScaffold w ten sposób:

  1. Użyj klasy, która reprezentuje treści do wybrania. Ta klasa powinna mieć wartość Parcelable, aby umożliwić zapisywanie i przywracanie wybranego elementu listy. Użyj wtyczki kotlin-parcelize, aby wygenerować kod.

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

  2. Utwórz ThreePaneScaffoldNavigator za pomocą rememberListDetailPaneScaffoldNavigator i dodaj BackHandler. Ten nawigator służy do przechodzenia między listą, panelem szczegółów i dodatkowymi panelami. Dzięki zadeklarowaniu typu ogólnego nawigator śledzi też stan rusztowania (czyli który element MyItem jest wyświetlany). Ponieważ ten typ jest parowalny, nawigator może zapisać i przywrócić stan, aby automatycznie obsługiwać zmiany konfiguracji. BackHandler obsługuje nawigację wstecz za pomocą gestu lub przycisku systemowego. Oczekiwane działanie przycisku Wstecz w ListDetailPaneScaffold zależy od rozmiaru okna i bieżącej wartości szablonu. Jeśli ListDetailPaneScaffold może wrócić do bieżącego stanu, canNavigateBack() jest true, co umożliwia BackHandler.

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

  3. Przekazuj wartość scaffoldState z komponentu navigator do komponentu ListDetailPaneScaffold.

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

  4. Przekaż implementację panelu list do ListDetailPaneScaffold. UżyjAnimatedPane, aby zastosować domyślne animacje paneli podczas nawigacji. Następnie użyj ThreePaneScaffoldNavigator, aby przejść do panelu szczegółów ListDetailPaneScaffoldRole.Detail, i wyświetlić przekazany element.

    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. ListDetailPaneScaffold umieść implementację panelu szczegółów. Po zakończeniu nawigacji currentDestination zawiera panel, do którego nawigowała aplikacja, w tym treści wyświetlane w tym panelu. Właściwość content ma ten sam typ, który został określony w pierwotnym wywołaniu funkcji remember (w tym przykładzie jest to MyItem), więc możesz też uzyskać do niej dostęp, aby wyświetlić dowolne dane.

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

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

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