Die Navigation beschreibt, wie sich Nutzer in Ihrer App bewegen. Sie interagieren mit UI-Elementen, in der Regel durch Tippen oder Klicken darauf, und die App reagiert mit der Anzeige neuer Inhalte. Wenn der Nutzer zum vorherigen Inhalt zurückkehren möchte, verwendet er die Zurück-Geste oder tippt auf den Button „Zurück“.
Navigationsstatus modellieren
Eine praktische Möglichkeit, dieses Verhalten zu modellieren, ist ein Stapel von Inhalten. Wenn der Nutzer vorwärts zu neuen Inhalten navigiert, werden diese oben auf den Stapel gelegt. Wenn er zurück von diesen Inhalten navigiert, werden sie vom Stapel entfernt und der vorherige Inhalt wird angezeigt. In der Navigation wird dieser Stapel normalerweise als Back-Stack bezeichnet, da er den Inhalt darstellt, zu dem der Nutzer zurückkehren kann.
Back-Stack erstellen
In Navigation 3 enthält der Back-Stack keine Inhalte. Stattdessen enthält er Verweise auf Inhalte, die als Schlüssel bezeichnet werden. Schlüssel können einen beliebigen Typ haben, sind aber in der Regel einfache, serialisierbare Datenklassen. Die Verwendung von Verweisen anstelle von Inhalten hat folgende Vorteile:
- Die Navigation ist einfach, da Schlüssel auf den Back-Stack gelegt werden.
- Solange die Schlüssel serialisierbar sind, kann der Back-Stack im nichtflüchtigen Speicher gespeichert werden, sodass er Konfigurationsänderungen und die Prozessbeendigung übersteht. Das ist wichtig, da Nutzer erwarten, dass sie Ihre App verlassen, später zurückkehren und dort weitermachen können, wo sie aufgehört haben, wobei derselbe Inhalt angezeigt wird. Weitere Informationen finden Sie unter Back-Stack speichern.
Ein wichtiges Konzept in der Navigation 3 API ist, dass Sie der Inhaber des Back-Stacks sind. Die Bibliothek:
- Erwartet, dass Ihr Back-Stack eine Snapshot-Status-gestützte
List<T>ist, wobeiTder Typ Ihres Back-Stackkeysist. Sie könnenAnyverwenden oder eigene, stärker typisierte Schlüssel bereitstellen. Wenn Sie die Begriffe „push“ oder „pop“ sehen, werden Elemente am Ende einer Liste hinzugefügt oder entfernt. - Beobachtet Ihren Back-Stack und spiegelt seinen Status in der UI mit einem
NavDisplaywider.
Das folgende Beispiel zeigt, wie Sie Schlüssel und einen Back-Stack erstellen und den Back-Stack als Reaktion auf Navigationsereignisse des Nutzers ändern:
// 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() }
Schlüssel in Inhalte auflösen
Inhalte werden in Navigation 3 mit NavEntry modelliert. Das ist eine Klasse, die eine zusammensetzbare Funktion enthält. Sie stellt ein Ziel dar – einen einzelnen Inhalt
, zu dem der Nutzer vorwärts und zurück navigieren kann.
Ein NavEntry kann auch Metadaten enthalten – Informationen zum Inhalt. Diese Metadaten können von Containerobjekten wie NavDisplay gelesen werden, um zu entscheiden, wie der Inhalt von NavEntry angezeigt werden soll. Metadaten können beispielsweise verwendet werden, um die Standardanimationen für einen bestimmten NavEntry zu überschreiben. metadata von NavEntry ist eine Map von String-Schlüsseln zu Any-Werten, die eine vielseitige Datenspeicherung ermöglicht.
Um einen key in einen NavEntry zu konvertieren, erstellen Sie einen Eintragsanbieter. Das ist eine Funktion, die einen key akzeptiert und einen NavEntry für diesen key zurückgibt. Sie wird normalerweise als Lambda-Parameter definiert, wenn ein NavDisplay erstellt wird.
Es gibt zwei Möglichkeiten, einen Eintragsanbieter zu erstellen: entweder direkt eine Lambda
Funktion erstellen oder die entryProvider-DSL verwenden.
Eintragsanbieterfunktion direkt erstellen
Normalerweise erstellen Sie eine Eintragsanbieterfunktion mit einer when-Anweisung, mit einem Zweig für jeden Ihrer Schlüssel.
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") } } } }
entryProvider-DSL verwenden
Die entryProvider-DSL kann Ihre Lambda-Funktion vereinfachen, da Sie nicht für jeden Ihrer Schlüsseltypen testen und für jeden einen NavEntry erstellen müssen.
Verwenden Sie dazu die Builder-Funktion entryProvider. Sie enthält auch ein Standard-Fallbackverhalten (Fehler auslösen), wenn der Schlüssel nicht gefunden wird.
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
Beachten Sie Folgendes im Snippet:
entrywird verwendet, um einenNavEntrymit dem angegebenen Typ und zusammensetzbaren Inhalt zu definieren.entryakzeptiert einenmetadata-Parameter, umNavEntry.metadatafestzulegen.
Back-Stack anzeigen
Der Back-Stack stellt den Navigationsstatus Ihrer App dar. Jedes Mal, wenn sich der Back-Stack ändert, sollte die App-UI den neuen Back-Stack-Status widerspiegeln. In Navigation 3 beobachtet ein NavDisplay Ihren Back-Stack und aktualisiert die UI entsprechend. Erstellen Sie ihn mit den folgenden Parametern:
- Ihr Back-Stack – er sollte vom Typ
SnapshotStateList<T>sein, wobeiTder Typ Ihrer Back-Stack-Schlüssel ist. Es ist eine beobachtbareList, sodass bei Änderungen eine Neukomposition vonNavDisplayausgelöst wird. - Ein
entryProvider, um die Schlüssel in Ihrem Back-Stack inNavEntry-Objekte zu konvertieren. - Optional können Sie ein Lambda für den Parameter
onBackangeben. Dieser wird aufgerufen, wenn der Nutzer ein Zurück-Ereignis auslöst.
Das folgende Beispiel zeigt, wie Sie ein NavDisplay erstellen:
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") } } } ) }
Standardmäßig zeigt NavDisplay den obersten NavEntry im Back-Stack in einem Layout mit einem einzelnen Bereich an. Die folgende Aufzeichnung zeigt diese App in Aktion:
NavDisplay Standardverhalten mit zwei
Zielen.Lebenszyklus des Ziels
NavDisplay verwendet benutzerdefinierte LifecycleOwners, um den Lebenszyklusstatus eines
NavEntry sowohl basierend auf Einschränkungen auf Szenen-Ebene als auch auf Einschränkungen auf Eintragsebene
zu begrenzen.
Weitere Informationen zu Lebenszyklen in Compose finden Sie unter Lebenszyklus in Jetpack Compose.
Einschränkungen des Lebenszyklus auf Szenenebene
NavDisplay verwaltet den Lebenszyklus der aktiven Scenes. Die Einschränkungen auf Szenenebene werden so bestimmt:
Für Szenen ohne Overlay:
RESUMED: Nur zulässig, wenn die Szenenübergänge abgeschlossen sind und keine aktiven Overlay-Szenen darüber angezeigt werden.STARTED: AufSTARTEDbegrenzt, während die Szenenübergänge stattfinden, z. B. beim Vorwärts- oder Zurücknavigieren oder wenn sie von einem Overlay verdeckt werden.
Für Overlay-Szenen wie Dialogfelder oder Bottom Sheets:
RESUMED: Nur für die oberste, derzeit aktive Overlay-Szene zulässig.STARTED: AufSTARTEDbegrenzt für alle darunter liegenden Overlay-Szenen, die von einem neueren Overlay verdeckt werden.
Lebenszyklusstatus auf Eintragsebene
Die Bibliothek verwaltet den maximalen Lebenszyklusstatus jedes einzelnen NavEntry basierend auf seiner Position im Back-Stack:
RESUMED: Wenn der Eintrag im aktuellen Back-Stack vorhanden ist, kann sein Lebenszyklus bis zuRESUMEDreichen (vorbehaltlich der Einschränkung auf Szenenebene).CREATED: Wenn sich der Eintrag nicht mehr im Back-Stack befindet, z. B. wenn er entfernt wurde, aber während der Animation noch auf dem Bildschirm gerendert wird, begrenzt die Bibliothek seinen Lebenszyklus streng aufCREATED. Diese Einschränkung sorgt dafür, dass Hintergrund- oder Beendigungseinträge die Ausführung aktiver Aufgaben wie das Erfassen von Abläufen oder das Starten von Coroutinen, die an die StatusRESUMEDoderSTARTEDgebunden sind, beenden, während sie ihre Beendigungsübergänge abschließen.
Kombination
Der endgültige Lebenszyklusstatus eines NavEntry wird beispielsweise so bestimmt:
| Szenario | Einschränkung auf Szenenebene | Einschränkung auf Eintragsebene | Effektive Einschränkung |
|---|---|---|---|
| Aktiver Eintrag, Bildschirm fertig (keine Übergänge oder Overlays) | RESUMED |
RESUMED |
RESUMED |
| Aktiver Eintrag, während des Übergangs (Navigieren zu oder von) | STARTED |
RESUMED |
STARTED |
| Aktiver Eintrag, von einem Overlay verdeckt (z. B. ein Dialogfeld ist geöffnet) | STARTED |
RESUMED |
STARTED |
| Entfernter Eintrag, Animation läuft | STARTED oder RESUMED |
CREATED |
CREATED |
Zusammenfassung
Das folgende Diagramm zeigt, wie Daten zwischen den verschiedenen Objekten in Navigation 3 fließen:
Navigationsereignisse lösen Änderungen aus. Als Reaktion auf Nutzerinteraktionen werden Schlüssel zum Back-Stack hinzugefügt oder daraus entfernt.
Änderungen am Back-Stack-Status lösen das Abrufen von Inhalten aus. Das
NavDisplay(eine zusammensetzbare Funktion, die einen Back-Stack rendert) beobachtet den Back-Stack. In der Standardkonfiguration wird der oberste Back-Stack-Eintrag in einem Layout mit einem einzelnen Bereich angezeigt. Wenn sich der oberste Schlüssel im Back-Stack ändert, verwendetNavDisplaydiesen Schlüssel, um den entsprechenden Inhalt vom Eintragsanbieter anzufordern.Eintragsanbieter liefert Inhalte. Der Eintragsanbieter ist eine Funktion, die einen Schlüssel in einen
NavEntryauflöst. Nachdem der Eintragsanbieter einen Schlüssel vonNavDisplayerhalten hat, liefert er den zugehörigenNavEntry, der sowohl den Schlüssel als auch den Inhalt enthält.Inhalte werden angezeigt.
NavDisplayempfängt denNavEntryund zeigt den Inhalt an.