Nawigacja to sposób, w jaki użytkownicy poruszają się po Twojej aplikacji. Wchodzą oni w interakcje z elementami interfejsu, zwykle klikając je lub naciskając, a aplikacja reaguje, wyświetlając nowe treści. Jeśli użytkownik chce wrócić do poprzednich treści, może użyć gestu cofania lub nacisnąć przycisk Wstecz.
Modelowanie stanu nawigacji
Wygodnym sposobem modelowania tego zachowania jest użycie stosu treści. Gdy użytkownik przechodzi dalej do nowych treści, są one umieszczane na wierzchu stosu. Gdy wraca z tych treści, są one zdejmowane ze stosu i wyświetlane są poprzednie treści. W terminologii nawigacji ten stos jest zwykle nazywany stosem wstecz , ponieważ reprezentuje treści, do których użytkownik może wrócić.
Tworzenie stosu wstecz
W Navigation 3 stos wstecz nie zawiera treści. Zawiera on odniesienia do treści, zwane kluczami. Klucze mogą być dowolnego typu, ale zwykle są to proste, serializowalne klasy danych. Używanie odniesień zamiast treści ma te zalety:
- Nawigacja jest prosta, ponieważ klucze są umieszczane na stosie wstecz.
- Dopóki klucze są serializowalne, stos wstecz można zapisać w pamięci trwałej, co pozwala mu przetrwać zmiany konfiguracji i zamknięcie procesu. Jest to ważne, ponieważ użytkownicy oczekują, że będą mogli opuścić aplikację, wrócić do niej później i kontynuować korzystanie z tych samych treści. Więcej informacji znajdziesz w artykule Zapisywanie stosu wstecz.
Kluczową koncepcją w interfejsie Navigation 3 API jest to, że jesteś właścicielem stosu wstecz. Biblioteka:
- oczekuje, że stos wstecz będzie obsługującym stan migawki
List<T>, gdzieTjest typem stosu wsteczkeys. Możesz użyć typuAnylub podać własne, bardziej ściśle określone klucze. Gdy widzisz terminy „push” lub „pop”, podstawowa implementacja polega na dodawaniu lub usuwaniu elementów z końca listy. - Obserwuje stos wstecz i odzwierciedla jego stan w interfejsie za pomocą elementu
NavDisplay.
W tym przykładzie pokazujemy, jak utworzyć klucze i stos wstecz oraz jak modyfikować stos wstecz w odpowiedzi na zdarzenia nawigacji użytkownika:
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
Rozwiązywanie kluczy do treści
Treści są modelowane w Navigation 3 za pomocą NavEntry, która jest klasą zawierającą funkcję typu „composable”. Reprezentuje ona miejsce docelowe – pojedynczą treść
, do której użytkownik może przejść dalej i wrócić.
Klasa NavEntry może też zawierać metadane – informacje o treści. Te metadane mogą być odczytywane przez obiekty kontenera, takie jak NavDisplay, aby pomóc im w podjęciu decyzji, jak wyświetlić zawartość klasy NavEntry. Metadane mogą na przykład służyć do zastępowania domyślnych animacji dla określonej klasy NavEntry. metadata klasy NavEntry to mapa kluczy String do wartości Any, która zapewnia wszechstronne przechowywanie danych.
Aby przekonwertować key na NavEntry, utwórz dostawcę wpisów. Jest to
funkcja, która przyjmuje key i zwraca NavEntry dla tego key. Zwykle jest ona definiowana jako parametr lambda podczas tworzenia NavDisplay.
Istnieją 2 sposoby tworzenia dostawcy wpisów: bezpośrednie utworzenie funkcji lambda
lub użycie entryProvider DSL.
Bezpośrednie tworzenie funkcji dostawcy wpisów
Funkcję dostawcy wpisów zwykle tworzy się za pomocą instrukcji when z gałęzią dla każdego klucza.
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
Używanie DSL entryProvider
DSL entryProvider może uprościć funkcję lambda, ponieważ nie trzeba testować każdego typu klucza ani tworzyć NavEntry dla każdego z nich.
Użyj do tego funkcji konstruktora entryProvider. Zawiera ona też domyślne zachowanie rezerwowe (zgłaszanie błędu), jeśli klucz nie zostanie znaleziony.
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
Zwróć uwagę na te elementy fragmentu kodu:
entrysłuży do definiowaniaNavEntryo danym typie i zawartości kompozycyjnej.entryakceptuje parametrmetadata, aby ustawićNavEntry.metadata.
Wyświetlanie stosu wstecz
Stos wstecz reprezentuje stan nawigacji aplikacji. Za każdym razem, gdy stos wstecz się zmienia, interfejs aplikacji powinien odzwierciedlać nowy stan stosu wstecz. W Navigation 3 element NavDisplay obserwuje stos wstecz i odpowiednio aktualizuje interfejs. Skonstruuj go za pomocą tych parametrów:
- Stos wstecz – powinien być typu
SnapshotStateList<T>, gdzieTjest typem kluczy stosu wstecz. Jest to obserwowalnaList, która powoduje rekompozycjęNavDisplaypo zmianie. entryProviderdo konwertowania kluczy w stosie wstecz na obiektyNavEntry.- Opcjonalnie możesz podać lambdę w parametrze
onBack. Jest ona wywoływana, gdy użytkownik wywoła zdarzenie cofania.
W tym przykładzie pokazujemy, jak utworzyć NavDisplay.
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
Domyślnie NavDisplay wyświetla najwyższy element NavEntry na stosie wstecz w układzie z jednym panelem. Na tym nagraniu widać działanie tej aplikacji:
NavDisplay domyślne działanie z dwoma
miejscami docelowymi.Cykl życia miejsca docelowego
NavDisplay używa niestandardowych elementów LifecycleOwners, aby ograniczyć stan cyklu życia
NavEntry na podstawie ograniczeń na poziomie sceny i ograniczeń na poziomie wpisu.
Więcej informacji o cyklach życia w Compose znajdziesz w artykule Cykl życia w Jetpack Compose.
Ograniczenia cyklu życia na poziomie sceny
NavDisplay zarządza cyklem życia aktywnych Scenes. Ograniczenia na poziomie sceny są określane w ten sposób:
W przypadku scen bez nakładek:
RESUMED: dozwolone tylko wtedy, gdy przejście sceny zostało zakończone i nie ma aktywnych scen nakładkowych wyświetlanych na jej wierzchu.STARTED: ograniczone doSTARTEDpodczas przejść sceny, np. podczas przechodzenia do przodu lub do tyłu albo gdy jest ona zasłonięta przez nakładkę.
W przypadku scen nakładkowych, takich jak okna dialogowe lub arkusze dolne:
RESUMED: dozwolone tylko w przypadku najwyższej, obecnie aktywnej sceny nakładkowej.STARTED: ograniczone doSTARTEDw przypadku wszystkich scen nakładkowych, które są zasłonięte przez nowszą nakładkę.
Stan cyklu życia na poziomie wpisu
Biblioteka zarządza maksymalnym stanem cyklu życia każdego NavEntry na podstawie jego obecności w stosie wstecz:
RESUMED: jeśli wpis znajduje się w bieżącym stosie wstecz, jego cykl życia może osiągnąć stanRESUMED(z zastrzeżeniem ograniczenia na poziomie sceny).CREATED: jeśli wpis nie znajduje się już w stosie wstecz, np. gdy został zdjęty ze stosu, ale nadal jest renderowany na ekranie podczas animacji, biblioteka ściśle ogranicza jego cykl życia do stanuCREATED. Ten limit zapewnia, że wpisy w tle lub wychodzące przestaną wykonywać aktywne zadania, takie jak zbieranie przepływów lub uruchamianie współprogramów powiązanych ze stanamiRESUMEDlubSTARTED, gdy zakończą przejścia wyjścia.
Jak się łączą
Na przykład ostateczny stan cyklu życia NavEntry jest określany w ten sposób:
| Scenariusz | Ograniczenie na poziomie sceny | Ograniczenie na poziomie wpisu | Efektywne ograniczenie |
|---|---|---|---|
| Aktywny wpis, ustabilizowany ekran (bez przejść ani nakładek) | RESUMED |
RESUMED |
RESUMED |
| Aktywny wpis, podczas przejścia (przechodzenie do lub z) | STARTED |
RESUMED |
STARTED |
| Aktywny wpis, zasłonięty przez nakładkę (np. otwarte okno dialogowe) | STARTED |
RESUMED |
STARTED |
| Wpis zdjęty ze stosu, animacja wyjścia | STARTED lub RESUMED |
CREATED |
CREATED |
Łączę wszystko w całość
Ten schemat pokazuje, jak dane przepływają między różnymi obiektami w Navigation 3:
Zmiany są inicjowane przez zdarzenia nawigacji. Klucze są dodawane do stosu wstecz lub z niego usuwane w odpowiedzi na interakcje użytkownika.
Zmiana stanu stosu wstecz powoduje pobieranie treści.
NavDisplay(kompozycja, która renderuje stos wstecz) obserwuje stos wstecz. W konfiguracji domyślnej wyświetla ona najwyższy wpis stosu wstecz w układzie z jednym panelem. Gdy zmieni się najwyższy klucz na stosie wstecz,NavDisplayużyje tego klucza, aby poprosić o odpowiednią treść od dostawcy wpisów.Dostawca wpisów dostarcza treści. Dostawca wpisów to funkcja, która rozwiązuje klucz do
NavEntry. Po otrzymaniu klucza odNavDisplaydostawca wpisów udostępnia powiązanyNavEntry, który zawiera zarówno klucz, jak i treść.Treść jest wyświetlana.
NavDisplayotrzymujeNavEntryi wyświetla treść.