Die Navigationskomponente bietet eine Kotlin-basierte domainspezifische Sprache (DSL), die auf den typsicheren Buildern von Kotlin basiert. Mit dieser API können Sie Ihren Graphen deklarativ in Ihrem Kotlin-Code und nicht in einer XML-Ressource erstellen. Dies kann nützlich sein, wenn Sie die
Navigation dynamisch gestalten. Ihre App könnte beispielsweise ein
von einem externen Webdienst abgerufen werden,
um dynamisch ein Navigationsdiagramm im
onCreate()
.
Abhängigkeiten
Fügen Sie zur Verwendung von Kotlin DSL mit Fragmenten die folgende Abhängigkeit zum
build.gradle
-Datei:
Groovy
dependencies { def nav_version = "2.9.2" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.2" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Diagramm erstellen
Hier ist ein einfaches Beispiel, das auf der Vorlage Sonnenblume
App. In diesem Fall
Beispiel mit zwei Zielen: home
und plant_detail
. Das home
Ziel ist vorhanden, wenn der Nutzer die App zum ersten Mal startet. Dieses Ziel
zeigt eine Liste der Pflanzen aus dem Garten der Nutzenden an. Wenn der Nutzer eine der
navigiert die App zum Ziel plant_detail
.
Abbildung 1 zeigt diese Ziele zusammen mit den Argumenten, die für das Ziel plant_detail
erforderlich sind, und einer Aktion, to_plant_detail
, mit der die App von home
zu plant_detail
wechselt.

home
und plant_detail
, sowie eine Aktion, die sie verbindet.Kotlin-DSL-Navigationsdiagramm hosten
Bevor Sie das Navigationsdiagramm für Ihre App erstellen können, müssen Sie einen Ort zum Hosten der
Diagramm. In diesem Beispiel werden Fragmente verwendet, sodass das Diagramm in einem NavHostFragment
innerhalb eines FragmentContainerView
gehostet wird:
<!-- activity_garden.xml -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</FrameLayout>
Beachten Sie, dass das Attribut app:navGraph
in diesem Beispiel nicht festgelegt ist. Das Diagramm
nicht als Ressource definiert,
den Ordner res/navigation
, daher muss er als Teil des onCreate()
festgelegt werden
in der Aktivität.
In XML verknüpft eine Aktion eine Ziel-ID mit einem oder mehreren Argumenten. Bei Verwendung der Navigations-DSL kann eine Route jedoch Argumente enthalten. Das bedeutet, dass bei Verwendung von DSL kein Konzept für Aktionen vorliegt.
Im nächsten Schritt definieren Sie die Routen, die Sie bei der Definition Ihrer Diagramm.
Routen für Ihr Diagramm erstellen
XML-basierte Navigationsdiagramme werden als Teil geparst.
des Android-Build-Prozesses. Für jedes im Diagramm definierte id
-Attribut wird eine numerische Konstante erstellt. Diese zur Build-Zeit generierten statischen IDs sind
beim Erstellen des Navigationsdiagramms zur Laufzeit
verfügbar, damit die Navigations-DSL
verwendet serialisierbare
anstelle von
IDs. Jede Route wird durch einen eindeutigen Typ dargestellt.
Beim Umgang mit Argumenten sind diese in die Route integriert Typ aus. So können Sie die Typsicherheit für Ihre Navigationsargumente gewährleisten.
@Serializable data object Home
@Serializable data class Plant(val id: String)
Diagramm mit NavGraphBuilder DSL erstellen
Nachdem Sie Ihre Routen definiert haben, können Sie den Navigationsgraphen erstellen.
val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
startDestination = Home
) {
fragment<HomeFragment, Home> {
label = resources.getString(R.string.home_title)
}
fragment<PlantDetailFragment, PlantDetail> {
label = resources.getString(R.string.plant_detail_title)
}
}
In diesem Beispiel werden zwei Fragmentziele mit der DSL-Builder-Funktion fragment()
definiert. Für diese Funktion sind zwei Typen
Argumente
.
Zuerst eine Fragment
-Klasse, die die Benutzeroberfläche für dieses Ziel bereitstellt. Das hat denselben Effekt wie das Festlegen des Attributs android:name
für Fragmentziele, die mit XML definiert sind.
Zweitens: die Route. Dies muss ein serialisierbarer Typ sein, der von Any
erweitert wird. Es
alle Navigationsargumente enthalten,
die von diesem Ziel verwendet werden,
und ihre Typen.
Die Funktion akzeptiert auch eine optionale Lambda-Funktion für die zusätzliche Konfiguration, z. B. als Ziellabel sowie eingebettete Builder-Funktionen für benutzerdefinierte und Deeplinks.
Mit der Kotlin-DSL-Grafik navigieren
Schließlich können Sie mit NavController.navigate()
-Aufrufen von home
zu plant_detail
wechseln:
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
In PlantDetailFragment
können Sie die Navigationsargumente abrufen, indem Sie Folgendes abrufen:
die aktuelle
NavBackStackEntry
und Anrufe
toRoute
um die Routeninstanz abzurufen.
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Wenn PlantDetailFragment
eine ViewModel
verwendet, rufe die Routen-Instanz mit SavedStateHandle.toRoute
ab.
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Im weiteren Verlauf dieses Leitfadens werden gängige Navigationsgrafikelemente, Ziele, und wie Sie diese beim Erstellen Ihrer Grafik verwenden.
Reiseziele
Kotlin DSL bietet integrierte Unterstützung für drei Zieltypen:
Fragment
-, Activity
- und NavGraph
-Ziele, von denen jedes ein eigenes Ziel hat
Inline-Erweiterungsfunktion zum Erstellen und Konfigurieren der
Ziel.
Fragmentziele
Die DSL-Funktion fragment()
kann mit der Fragmentklasse für die Benutzeroberfläche und dem Routentyp parametrisiert werden, der zum eindeutigen Identifizieren dieses Ziels verwendet wird, gefolgt von einem Lambda, in dem Sie eine zusätzliche Konfiguration angeben können, wie im Abschnitt Mit dem Kotlin-DSL-Graphen navigieren beschrieben.
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
Aktivitätsziel
Die DSL-Funktion activity()
nimmt einen Typparameter für die Route an, ist aber nicht für eine implementierende Aktivitätsklasse parametrisiert. Stattdessen legen Sie ein optionales activityClass
in einem nachgestellten Lambda fest. Dank dieser Flexibilität können Sie ein Aktivitätsziel für eine Aktivität definieren, die mit einem impliziten Intent gestartet werden soll, für den eine explizite Aktivitätsklasse keinen Sinn ergibt. Wie bei Fragmentzielen können Sie auch ein Label, benutzerdefinierte Argumente und Deeplinks konfigurieren.
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
Ziel der Navigationsgrafik
Mit der DSL-Funktion navigation()
können Sie einen verschachtelten Navigationsgraphen erstellen. Diese Funktion nimmt einen Typparameter für die Route an, die diesem Graphen zugewiesen werden soll. Außerdem sind zwei Argumente erforderlich:
die Route des Ausgangsorts des Diagramms und eine Lambda-Funktion zum
um das Diagramm zu konfigurieren. Zu den gültigen Elementen gehören andere Ziele, benutzerdefinierte Argumenttypen, Deeplinks und ein beschreibendes Label für das Ziel.
Dieses Label kann nützlich sein, um den Navigationsgraphen mit UI-Komponenten über NavigationUI
zu verknüpfen.
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
Unterstützung benutzerdefinierter Ziele
Wenn Sie einen neuen Zieltyp verwenden, der die Kotlin-DSL nicht direkt unterstützt, können Sie diese Ziele mit addDestination()
in Ihre Kotlin-DSL einfügen:
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
Alternativ können Sie auch den unären Plusoperator verwenden, um ein neu erstelltes Ziel direkt zum Diagramm hinzuzufügen:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
Zielargumente bereitstellen
Zielargumente können als Teil der Routenklasse definiert werden. Sie können genauso definiert werden wie bei jeder anderen Kotlin-Klasse. Erforderliche Argumente werden als nicht nullwertige Typen definiert und optionale Argumente mit Standardwerten.
Der zugrunde liegende Mechanismus zum Darstellen von Routen und ihren Argumenten ist ein String
basiert. Durch die Verwendung von Strings zum Modellieren von Routen kann der Navigationsstatus gespeichert und
während der Konfiguration vom Laufwerk wiederhergestellt
Änderungen und vom System initiierter Prozess
Tod. Aus diesem Grund
Jedes Navigationsargument muss serialisierbar sein, das heißt, es sollte
-Methode, die die speicherinterne Darstellung des Argumentwerts in einen
String
Das Kotlin-Serialisierungs-Plug-in generiert automatisch Serialisierungsmethoden für Grundtypen, wenn einem Objekt die @Serializable
-Anmerkung hinzugefügt wird.
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
Benutzerdefinierte Typen bereitstellen
Für benutzerdefinierte Argumenttypen müssen Sie eine benutzerdefinierte NavType
-Klasse angeben. Dieses
lässt sich genau steuern, wie Ihr Typ von einer Route oder einem Deeplink geparst wird.
Zum Beispiel könnte eine Route, die zur Definition eines Suchbildschirms verwendet wird, eine Klasse enthalten, die steht für die Suchparameter:
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
@Parcelize
data class SearchParameters(
val searchQuery: String,
val filters: List<String>
)
Eine benutzerdefinierte NavType
könnte folgendermaßen geschrieben werden:
val SearchParametersType = object : NavType<SearchParameters>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: SearchParameters) {
bundle.putParcelable(key, value)
}
override fun get(bundle: Bundle, key: String): SearchParameters {
return bundle.getParcelable(key) as SearchParameters
}
override fun serializeAsValue(value: SearchParameters): String {
// Serialized values must always be Uri encoded
return Uri.encode(Json.encodeToString(value))
}
override fun parseValue(value: String): SearchParameters {
// Navigation takes care of decoding the string
// before passing it to parseValue()
return Json.decodeFromString<SearchParameters>(value)
}
}
Dieser kann dann wie jeder andere Typ in Ihrer Kotlin-DSL verwendet werden:
fragment<SearchFragment, SearchRoute>(
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
) {
label = getString(R.string.plant_search_title)
}
Erstellen Sie während der Navigation zum Ziel eine Instanz Ihrer Route:
val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))
Der Parameter kann aus der Route im Ziel abgerufen werden:
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
Deeplinks
Deeplinks können wie bei einem XML-gestützten Navigationsgraphen jedem Ziel hinzugefügt werden. Alle Verfahren, die unter Deeplink für ein Ziel erstellen beschrieben sind, gelten auch für das Erstellen eines Deeplinks mit der Kotlin-DSL.
Beim Erstellen eines impliziten Deeplinks
Sie haben jedoch keine XML-Navigationsressource, die analysiert werden kann,
<deepLink>
-Elemente. Daher können Sie sich nicht darauf verlassen, <nav-graph>
-Element in der Datei AndroidManifest.xml
und muss stattdessen intent hinzufügen
Filter manuell zu Ihren Aktivitäten hinzufügen. Der von Ihnen angegebene Intent-Filter muss mit dem Basispfad, der Aktion und dem MIME-Typ der Deeplinks Ihrer App übereinstimmen.
Zum Hinzufügen von Deeplinks zu einem Ziel wird die darin enthaltene Funktion deepLink
aufgerufen
die Lambda-Funktion des Ziels. Sie akzeptiert die Route als parametrisierten Typ und einen
basePath
für den Basispfad der für den Deeplink verwendeten URL.
Du kannst auch eine Aktion und einen MIME-Typ mit dem abschließenden Lambda deepLinkBuilder
hinzufügen.
Im folgenden Beispiel wird ein Deeplink-URI für das Ziel Home
erstellt.
@Serializable data object Home
fragment<HomeFragment, Home>{
deepLink<Home>(basePath = "www.example.com/home"){
// Optionally, specify the action and/or mime type that this destination
// supports
action = "android.intent.action.MY_ACTION"
mimeType = "image/*"
}
}
URI-Format
Das Deeplink-URI-Format wird automatisch aus den Feldern der Route gemäß den folgenden Regeln generiert:
- Erforderliche Parameter werden als Pfadparameter angehängt (Beispiel:
/{id}
). - Parameter mit einem Standardwert (optionale Parameter) werden als Abfrage
Parameter (Beispiel:
?name={name}
) - Sammlungen werden als Abfrageparameter angehängt (Beispiel:
?items={value1}&items={value2}
). - Die Reihenfolge der Parameter entspricht der Reihenfolge der Felder in der Route.
Beispiel für einen Routentyp:
@Serializable data class PlantDetail(
val id: String,
val name: String,
val colors: List<String>,
val latinName: String? = null,
)
hat das folgende generierte URI-Format:
basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}
Die Anzahl der Deeplinks, die Sie hinzufügen können, ist nicht begrenzt. Bei jedem Aufruf von deepLink()
wird einer Liste, die für dieses Ziel verwaltet wird, ein neuer Deeplink hinzugefügt.
Beschränkungen
Das Plug-in Sichere Args ist
nicht mit Kotlin DSL kompatibel, da das Plug-in nach XML-Ressourcendateien sucht,
Generieren der Klassen Directions
und Arguments
.