Tworzenie układu ze szczegółami listy

Wzór interfejsu użytkownika typu lista-szczegóły to układ dwupanelowy, w którym jeden panel zawiera listę elementów, a drugi wyświetla szczegóły elementów wybranych z listy.

Ten wzorzec jest szczególnie przydatny w przypadku aplikacji, które zawierają szczegółowe informacje o elementach dużych zbiorów, np. klienta poczty e-mail, który ma listę e-maili i szczegółową treść każdej wiadomości. Szczegółowa lista może też być używana w przypadku mniej istotnych ścieżek, np. dzieląc ustawienia aplikacji na listę kategorii z ustawieniami każdej kategorii w panelu szczegółów.

Panel szczegółów wyświetlany obok strony z listą.
Rysunek 1. Gdy jest wystarczająco dużo miejsca na ekranie, okienko szczegółów jest wyświetlane obok okienka 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ń.

Implementowanie wzorca lista-szczegóły za pomocą NavigableListDetailPaneScaffold

NavigableListDetailPaneScaffold to funkcja kompozycyjna, która upraszcza implementację układu szczegółowa lista w Jetpack Compose. Zawiera element ListDetailPaneScaffold i dodaje wbudowaną nawigację oraz animacje przewidywanego przejścia wstecz.

Szablon szczegółowej listy obsługuje maksymalnie 3 panele:

  1. Panel listy: wyświetla kolekcję elementów.
  2. Okienko szczegółów: wyświetla szczegóły wybranego elementu.
  3. Dodatkowy panel (opcjonalny): w razie potrzeby zapewnia dodatkowy kontekst.

Szkielet dostosowuje się do rozmiaru okna:

  • W dużych oknach panele listy i szczegółów są wyświetlane obok siebie.
  • W małych oknach widoczny jest tylko jeden panel naraz, który zmienia się w miarę poruszania się użytkownika.

Deklarowanie zależności

NavigableListDetailPaneScaffold jest częścią biblioteki adaptacyjnej nawigacji Material 3.

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

Kotlin

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

Dynamiczny

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • adaptacyjne: elementy składowe niskiego poziomu, takie jak HingeInfo i Posture;
  • adaptive-layout: układy adaptacyjne, np. ListDetailPaneScaffoldSupportingPaneScaffold;
  • adaptive-navigation: komponenty kompozycyjne do nawigacji w ramach paneli i między nimi, a także układy adaptacyjne, które domyślnie obsługują nawigację, np. NavigableListDetailPaneScaffoldNavigableSupportingPaneScaffold;

Upewnij się, że projekt zawiera compose-material3-adaptive w wersji 1.1.0-beta1 lub nowszej.

Włączanie gestu przewidywanego przejścia wstecz

Aby włączyć animacje przewidywanego przejścia wstecz w Androidzie 15 lub starszym, musisz wyrazić zgodę na obsługę gestu przewidywanego przejścia wstecz. Aby wyrazić zgodę, dodaj android:enableOnBackInvokedCallback="true" do tagu <application> lub poszczególnych tagów <activity> w pliku AndroidManifest.xml. Więcej informacji znajdziesz w artykule Włączanie gestu przewidywania powrotu.

Gdy aplikacja jest kierowana na Androida 16 (API na poziomie 36) lub nowszego, funkcja przewidywanego przejścia wstecz jest domyślnie włączona.

Podstawowe użycie

Zaimplementuj NavigableListDetailPaneScaffold w ten sposób:

  1. Użyj klasy, która reprezentuje wybrane treści. Użyj klasy Parcelable, aby umożliwić zapisywanie i przywracanie wybranego elementu listy. Użyj wtyczki kotlin-parcelize, aby wygenerować kod.
  2. Utwórz ThreePaneScaffoldNavigator za pomocą rememberListDetailPaneScaffoldNavigator.

Ten nawigator służy do przechodzenia między listą, panelem szczegółów i dodatkowymi panelami. Deklarując typ ogólny, nawigator śledzi też stan elementu Scaffold (czyli który widżet MyItem jest wyświetlany). Ten typ można przekazywać, więc nawigator może zapisywać i przywracać stan, aby automatycznie obsługiwać zmiany konfiguracji.

  1. Przekaż nawigatora do komponentu NavigableListDetailPaneScaffold.

  2. Prześlij implementację panelu listy do NavigableListDetailPaneScaffold. Użyj AnimatedPane, aby podczas nawigacji zastosować domyślne animacje paneli. Następnie użyj ThreePaneScaffoldNavigator do przejścia do okienka szczegółów ListDetailPaneScaffoldRole.Detail i wyświetl przekazany element.

  3. Umieść implementację panelu szczegółów w NavigableListDetailPaneScaffold.

Po zakończeniu nawigacji zmienna currentDestination zawiera panel, do którego aplikacja przeszła, w tym treść wyświetlaną w tym panelu. contentKey Właściwość jest tego samego typu, co określony w pierwotnym wywołaniu, więc możesz uzyskać dostęp do wszystkich danych, które chcesz wyświetlić.

  1. Opcjonalnie zmień wartość defaultBackBehavior w pliku NavigableListDetailPaneScaffold. Domyślnie usługa NavigableListDetailPaneScaffold używa PopUntilScaffoldValueChange w przypadku defaultBackBehavior.

Jeśli Twoja aplikacja wymaga innego wzorca przechodzenia wstecz, możesz zastąpić to zachowanie, określając inną opcję BackNavigationBehavior.

BackNavigationBehavior opcji

W tej sekcji użyjemy przykładu aplikacji do poczty e-mail z listą e-maili w jednym panelu i widokiem szczegółowym w drugim.

Dotyczy to zmian w ogólnej strukturze układu. W konfiguracji wielopanelowej zmiana treści e-maila w panelu szczegółowym nie zmienia struktury układu. Dlatego przycisk Wstecz może spowodować zamknięcie aplikacji lub bieżącego wykresu nawigacji, ponieważ w bieżącym kontekście nie ma zmiany układu, do której można by wrócić. W układzie z jednym panelem naciśnięcie przycisku Wstecz spowoduje pominięcie zmian treści w widoku szczegółowym i powrót do widoku listy, ponieważ jest to wyraźna zmiana układu.

Oto przykłady:

  • Wielopanelowy: w panelu szczegółów wyświetlasz e-maila (element 1). Kliknięcie innego e-maila (element 2) powoduje zaktualizowanie panelu szczegółów, ale panele listy i szczegółów pozostają widoczne. Naciśnięcie przycisku Wstecz może spowodować zamknięcie aplikacji lub bieżącego przepływu nawigacji.
  • Jeden panel: wyświetlasz element 1, a następnie element 2. Naciśnięcie przycisku Wstecz spowoduje powrót bezpośrednio do panelu listy e-maili.

Używaj tego ustawienia, gdy chcesz, aby użytkownicy postrzegali wyraźne przejścia układu przy każdym działaniu „Wstecz”.

Ilustracja pokazująca zmiany wartości nawigacji w różnych panelach układu.
PopUntilContentChange

Takie działanie nadaje priorytet wyświetlanym treściom. Jeśli wyświetlisz element 1, a potem element 2, naciśnięcie przycisku Wstecz spowoduje powrót do elementu 1, niezależnie od układu.

Oto przykłady:

  • Wielopanelowy: wyświetlasz produkt 1 w panelu szczegółów, a następnie klikasz produkt 2 na liście. Okienko szczegółów zostanie zaktualizowane. Naciśnięcie przycisku Wstecz przywróci okienko szczegółów do Elementu 1.
  • Jeden panel: następuje przywrócenie tej samej treści.

Używaj tego, gdy użytkownik oczekuje, że po kliknięciu przycisku Wstecz wróci do wcześniej wyświetlanych treści.

przejście między dwoma panelami szczegółów,
PopUntilCurrentDestinationChange

To działanie powoduje usunięcie z powrotem elementów z listy wstecznej, dopóki nie zmieni się bieżące miejsce docelowe nawigacji. Dotyczy to zarówno układów z 1 panelem, jak i z wieloma panelami.

Oto przykłady:

Niezależnie od tego, czy korzystasz z układu z 1 czy z wieloma panelami, naciśnięcie przycisku Wstecz zawsze przenosi fokus z zaznaczonego elementu nawigacyjnego do poprzedniego miejsca docelowego. W naszej aplikacji do poczty e-mail oznacza to, że zmieni się wizualne wskazanie wybranego panelu.

Używaj tej opcji, gdy dla wygody użytkowników kluczowe jest zachowanie wyraźnego wizualnego wskazania bieżącej nawigacji.

poruszanie się między panelami szczegółów i listy;
PopLatest

Ta opcja usuwa z listy tylko ostatnie miejsce docelowe. Użyj tej opcji, aby cofnąć się bez pomijania stanów pośrednich.

Po wykonaniu tych czynności kod powinien wyglądać podobnie do tego:

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
                        )

                    }
                }
            )
        }
    }