Grundlagen verstehen und implementieren

Die Navigation beschreibt, wie sich Nutzer in Ihrer App bewegen. Nutzer interagieren mit UI-Elementen, in der Regel durch Tippen oder Klicken, und die App reagiert, indem sie neue Inhalte anzeigt. Wenn der Nutzer zum vorherigen Inhalt zurückkehren möchte, verwendet er die Touch-Geste „Zurück“ oder tippt auf den Button „Zurück“.

Navigationsstatus modellieren

Eine bequeme Möglichkeit, dieses Verhalten zu modellieren, ist ein Stapel von Inhalten. Wenn der Nutzer vorwärts zu neuen Inhalten navigiert, werden diese oben auf dem Stack abgelegt. Wenn sie von diesem Inhalt zurückkehren, wird er aus dem Stack entfernt und der vorherige Inhalt wird angezeigt. In der Navigation wird dieser Stack normalerweise als Back Stack bezeichnet, da er die Inhalte darstellt, zu denen der Nutzer zurückkehren kann.

Ein rot eingekreister Aktionsbutton auf der Bildschirmtastatur (ein Häkchensymbol).
Abbildung 1. Diagramm, das zeigt, wie sich der Back Stack bei Navigationsereignissen des Nutzers ändert.

Back Stack erstellen

In Navigation 3 enthält der Back Stack keine Inhalte. Stattdessen enthält sie 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 Referenzen anstelle von Inhalten bietet folgende Vorteile:

  • Die Navigation ist einfach, da Tasten auf den Back Stack verschoben werden.
  • Solange die Schlüssel serialisierbar sind, kann der Back Stack im nichtflüchtigen Speicher gespeichert werden, sodass er Konfigurationsänderungen und Prozessbeendigungen übersteht. Das ist wichtig, weil Nutzer erwarten, dass sie Ihre App verlassen, später zurückkehren und dort weitermachen können, wo sie aufgehört haben, wobei dieselben Inhalte angezeigt werden. Weitere Informationen findest du unter Back Stack speichern.

Ein wichtiges Konzept der Navigation 3 API ist, dass Sie den Back Stack selbst verwalten. Die Bibliothek:

  • Es wird erwartet, dass der Back Stack ein Snapshot-Status-gestütztes List<T> ist, wobei T der Typ des Back Stack-keys ist. Sie können Any verwenden oder eigene, stärker typisierte Schlüssel bereitstellen. Wenn Sie die Begriffe „push“ oder „pop“ sehen, bedeutet das, dass Elemente am Ende einer Liste hinzugefügt oder entfernt werden.
  • Beobachtet den Back Stack und spiegelt seinen Status in der Benutzeroberfläche mithilfe eines NavDisplay wider.

Im folgenden Beispiel wird gezeigt, 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

In Navigation 3 werden Inhalte mit NavEntry modelliert. Das ist eine Klasse, die eine zusammensetzbare Funktion enthält. Es stellt ein Ziel dar – einen einzelnen Inhalt, zu dem der Nutzer vorwärts und von dem er zurück navigieren kann.

Ein NavEntry kann auch Metadaten enthalten, also Informationen über den Inhalt. Diese Metadaten können von Containerobjekten wie NavDisplay gelesen werden, um zu entscheiden, wie der Inhalt des NavEntry angezeigt werden soll. Metadaten können beispielsweise verwendet werden, um die Standardanimationen für ein bestimmtes NavEntry zu überschreiben. NavEntry metadata ist eine Zuordnung von String-Schlüsseln zu Any-Werten und bietet eine vielseitige Datenspeicherung.

Wenn Sie einen key in einen NavEntry konvertieren möchten, erstellen Sie einen Eintragsprovider. Diese Funktion akzeptiert einen key und gibt einen NavEntry für diesen key zurück. Sie wird normalerweise als Lambda-Parameter beim Erstellen eines NavDisplay definiert.

Es gibt zwei Möglichkeiten, einen Entry Provider zu erstellen: entweder durch direktes Erstellen einer Lambda-Funktion oder mithilfe der entryProvider-DSL.

Eintragsanbieterfunktion direkt erstellen

In der Regel erstellen Sie eine Entry Provider-Funktion mit einer when-Anweisung, die für jeden Schlüssel einen Zweig enthält.

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

Mit der entryProvider-DSL können Sie Ihre Lambda-Funktion vereinfachen, da Sie nicht jeden Schlüsseltyp testen und für jeden einen NavEntry erstellen müssen. Verwenden Sie dazu die Builder-Funktion entryProvider. Außerdem wird ein Standard-Fallback-Verhalten (Ausgabe eines Fehlers) verwendet, 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:

  • entry wird verwendet, um ein NavEntry mit dem angegebenen Typ und dem zusammensetzbaren Inhalt zu definieren.
  • entry akzeptiert einen metadata-Parameter, um NavEntry.metadata festzulegen.

Back Stack anzeigen

Der Back Stack stellt den Navigationsstatus Ihrer App dar. Wenn sich der Back Stack ändert, sollte die App-Benutzeroberfläche den neuen Back Stack-Status widerspiegeln. In Navigation 3 beobachtet ein NavDisplay Ihren Back Stack und aktualisiert die Benutzeroberfläche entsprechend. Erstellen Sie sie mit den folgenden Parametern:

  • Ihr Back Stack – dieser sollte vom Typ SnapshotStateList<T> sein, wobei T der Typ Ihrer Back Stack-Schlüssel ist. Es ist ein beobachtbares List, sodass bei einer Änderung die Neuzusammensetzung von NavDisplay ausgelöst wird.
  • Ein entryProvider, um die Schlüssel im Back Stack in NavEntry-Objekte zu konvertieren.
  • Optional können Sie einen Lambda-Wert für den Parameter onBack angeben. Diese wird aufgerufen, wenn der Nutzer ein Zurück-Ereignis auslöst.

Im folgenden Beispiel wird gezeigt, wie Sie eine 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 die NavDisplay die oberste NavEntry im Back Stack in einem Layout mit einem einzelnen Bereich an. Die folgende Aufnahme zeigt die App in Aktion:

Standardverhalten von `NavDisplay` mit zwei Zielen.
Abbildung 2: NavDisplay-Standardverhalten mit zwei Zielen.

Lebenszyklus des Ziels

NavDisplay verwendet benutzerdefinierte LifecycleOwners, um den Lebenszyklusstatus einer NavEntry sowohl auf Scene-Ebene als auch auf Entry-Ebene zu begrenzen.

Weitere Informationen zu Lebenszyklen in Compose finden Sie unter Lebenszyklus in Jetpack Compose.

Lebenszykluseinschränkungen auf Szenenebene

NavDisplay verwaltet den Lebenszyklus der aktiven Scenes. Obergrenzen auf Szenenebene werden so ermittelt:

Für Szenen ohne Overlay:

  • RESUMED: Nur zulässig, wenn der Szenenübergang abgeschlossen ist und keine aktiven Overlay-Szenen darüber angezeigt werden.
  • STARTED: Begrenzt auf STARTED während Szenenübergängen, z. B. beim Vor- oder Zurückgehen oder wenn die Szene von einem Overlay verdeckt wird.

Für Overlay-Szenen wie Dialogfelder oder Ansichten am unteren Rand:

  • RESUMED: Nur für die oberste, derzeit aktive Overlay-Szene zulässig.
  • STARTED: Begrenzt auf STARTED für alle zugrunde liegenden Overlay-Szenen, die von einem neueren Overlay abgedeckt werden.

Lebenszyklusstatus der Einstiegsklasse

Die Bibliothek verwaltet den maximalen Lebenszyklusstatus jedes einzelnen NavEntry basierend auf seinem Vorhandensein im Back Stack:

  • RESUMED: Wenn der Eintrag im aktuellen Back Stack vorhanden ist, kann sein Lebenszyklus bis zu RESUMED reichen (vorbehaltlich der Obergrenze 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 den Lebenszyklus strikt auf CREATED. Dieses Limit sorgt dafür, dass Hintergrund- oder Beendigungseinträge keine aktiven Aufgaben mehr ausführen, z. B. das Erfassen von Abläufen oder das Starten von Koroutinen, die an die Status RESUMED oder STARTED gebunden sind, während sie ihre Beendigungsübergänge abschließen.

Kombination

Der endgültige Lebenszyklusstatus eines NavEntry wird beispielsweise so aufgelöst:

Szenario Obergrenze auf Szenenebene Einstiegskappe Effektive Obergrenze
Aktiver Eintrag, Bildschirm mit abgeschlossener Aktion (keine Übergänge oder Overlays) RESUMED RESUMED RESUMED
Aktiver Eintrag, während des Übergangs (Navigation zu oder von) STARTED RESUMED STARTED
Aktiver Eintrag, der von einem Overlay verdeckt wird (z. B. wenn ein Dialogfeld geöffnet ist) STARTED RESUMED STARTED
Eintrag wird ausgeblendet STARTED oder RESUMED CREATED CREATED

Zusammenfassung

Das folgende Diagramm zeigt, wie Daten zwischen den verschiedenen Objekten in Navigation 3 fließen:

Eine Visualisierung des Datenflusses zwischen den verschiedenen Objekten in Navigation 3.
Abbildung 3: Diagramm, das zeigt, wie Daten durch verschiedene Objekte in Navigation 3 fließen.
  1. Navigationsereignisse initiieren Änderungen. Schlüssel werden als Reaktion auf Nutzerinteraktionen zum Back Stack hinzugefügt oder daraus entfernt.

  2. Änderung des Back-Stack-Status löst Abruf von Inhalten aus Die 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, verwendet der NavDisplay diesen Schlüssel, um den entsprechenden Inhalt vom Eintragsprovider anzufordern.

  3. Eintragsanbieter stellt Inhalte bereit. Der Eintragsprovider ist eine Funktion, die einen Schlüssel in ein NavEntry auflöst. Wenn der Eintragsprovider einen Schlüssel vom NavDisplay empfängt, stellt er den zugehörigen NavEntry bereit, der sowohl den Schlüssel als auch den Inhalt enthält.

  4. Inhalte werden angezeigt. Der NavDisplay empfängt die NavEntry und zeigt den Inhalt an.