Die Navigationskomponente bietet eine Kotlin-basierte domainspezifische Sprache oder
DSL, das auf den Kotlin-Standards
typsichere Builder.
Mit dieser API können Sie
Ihre Grafik deklarativ in Ihrem Kotlin-Code erstellen,
und nicht in einer XML-Ressource. Dies kann nützlich sein, wenn Sie
die Navigation Ihrer App dynamisch gestalten. Ihre App könnte beispielsweise
eine Navigationskonfiguration von einem externen Webdienst im Cache
um dynamisch ein Navigationsdiagramm im
onCreate()
.
Abhängigkeiten
Fügen Sie zur Verwendung von Kotlin DSL die folgende Abhängigkeit zum
build.gradle
-Datei:
Groovig
dependencies { def nav_version = "2.7.7" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.7.7" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Diagramm erstellen
Beginnen wir mit einem einfachen Beispiel
Sunflower 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 für den Parameter
plant_detail
-Ziel und die von der App verwendete Aktion to_plant_detail
um von home
nach plant_detail
zu gelangen.
Kotlin-DSL-Navigationsdiagramm hosten
Bevor Sie das Navigationsdiagramm für Ihre App erstellen können, benötigen Sie einen Host,
in der Grafik. Da in diesem Beispiel Fragmente verwendet werden, wird die Grafik
NavHostFragment
innerhalb einer
FragmentContainerView
:
<!-- 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
ist nicht definiert als
resource im Feld
res/navigation
-Ordner, 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 des Navigations-DSL kann eine Route jedoch Argumente als Teil der Route. Das bedeutet, dass bei Verwendung von DSL kein Konzept für Aktionen vorliegt.
Im nächsten Schritt definieren Sie einige Konstanten, in der Grafik.
Konstanten für die Grafik erstellen
XML-basierte Navigationsdiagramme
im Rahmen des Android-Build-Prozesses geparst. Eine numerische Konstante wird
für jedes in der Grafik definierte id
-Attribut. Diese zur Build-Zeit generierten statischen
IDs sind beim Erstellen des Navigationsdiagramms während der Laufzeit nicht verfügbar.
Navigations-DSL verwendet Routenstrings anstelle von IDs. Jede Route wird durch
eine eindeutige Zeichenfolge. Es empfiehlt sich, diese als Konstanten zu definieren,
Fehler aufgrund von Tippfehlern.
Beim Umgang mit Argumenten sind dies im Routenstring integriert ist. Die Einbindung dieser Logik in die Route kann, wie gesagt, das Risiko Fehler aufgrund von Tippfehlern.
object nav_routes {
const val home = "home"
const val plant_detail = "plant_detail"
}
object nav_arguments {
const val plant_id = "plant_id"
const val plant_name = "plant_name"
}
Diagramm mit NavGraphBuilder DSL erstellen
Nachdem Sie Ihre Konstanten definiert haben, können Sie die Navigation Diagramm.
val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
startDestination = nav_routes.home
) {
fragment<HomeFragment>(nav_routes.home) {
label = resources.getString(R.string.home_title)
}
fragment<PlantDetailFragment>("${nav_routes.plant_detail}/{${nav_arguments.plant_id}}") {
label = resources.getString(R.string.plant_detail_title)
argument(nav_arguments.plant_id) {
type = NavType.StringType
}
}
}
In diesem Beispiel definiert die nachgestellte Lambda-Funktion zwei Fragmentziele mithilfe des
fragment()
DSL-Builder-Funktion Für diese Funktion ist ein Routenstring für das Ziel erforderlich.
die sich aus den Konstanten ergibt. Die Funktion akzeptiert auch eine optionale
Lambda für die zusätzliche Konfiguration, z. B. für das Ziellabel,
eingebetteten Builder-Funktionen für Argumente und Deeplinks.
Die Klasse Fragment
, die
die UI jedes Ziels verwaltet, wird im Inneren als parametrisierter Typ übergeben.
spitze Klammern (<>
) Dies hat denselben Effekt wie das Festlegen von android:name
bei Fragmentzielen, die mit XML definiert sind.
Mit der Kotlin-DSL-Grafik navigieren
Schließlich können Sie mit der Standardmethode von home
nach plant_detail
navigieren.
NavController.Navigate()
Anrufe:
private fun navigateToPlant(plantId: String) {
findNavController().navigate("${nav_routes.plant_detail}/$plantId")
}
In PlantDetailFragment
können Sie den Wert des Arguments so abrufen:
im folgenden Beispiel:
val plantId: String? = arguments?.getString(nav_arguments.plant_id)
Wie Sie beim Navigieren Argumente angeben, erfahren Sie in der Bereitstellen von Zielargumenten.
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
fragment()
Die DSL-Funktion kann auf die implementierende Fragmentklasse parametrisiert werden und nimmt eine
eindeutiger Routenstring, der diesem Ziel zugewiesen werden soll, gefolgt von einer Lambda-Funktion, wobei
können Sie zusätzliche Konfigurationen vornehmen, wie in den
Mit der Kotlin-DSL-Grafik navigieren
.
fragment<FragmentDestination>(nav_routes.route_name) {
label = getString(R.string.fragment_title)
// arguments, deepLinks
}
Ziel der Aktivität
Die activity()
Die DSL-Funktion verwendet eine eindeutige Routenzeichenfolge, die diesem Ziel zugewiesen wird,
nicht parametrisiert für eine Implementierungs-Aktivitätsklasse. Stattdessen legen Sie eine
optionales activityClass
in einer nachfolgenden Lambda-Funktion. Dank dieser Flexibilität können Sie
ein Aktivitätsziel für eine Aktivität definieren, die mit einem
impliziter Intent, wobei ein
wäre keine explizite Aktivität. Wie bei Fragmentzielen
können Sie auch ein Label,
Argumente und Deeplinks konfigurieren.
activity(nav_routes.route_name) {
label = getString(R.string.activity_title)
// arguments, deepLinks...
activityClass = ActivityDestination::class
}
Ziel der Navigationsgrafik
Die navigation()
Mit der DSL-Funktion lässt sich
verschachteltes Navigationsdiagramm.
Diese Funktion verwendet drei Argumente: eine Route zu
der Grafik die Route des Startziels der Grafik
Lambda zur weiteren Konfiguration des Graphen. Gültige Elemente sind andere Ziele,
Argumente, Deeplinks und eine
beschreibendes Label für das Ziel.
Dieses Label kann nützlich sein, um das Navigationsdiagramm an die Benutzeroberfläche zu binden
Komponenten mithilfe von
Navigations-UI
navigation("route_to_this_graph", nav_routes.home) {
// label, other destinations, deep links
}
Unterstützung benutzerdefinierter Ziele
Wenn Sie eine
neuen Zieltyp, der keine
Kotlin DSL direkt unterstützen, können Sie diese Ziele Ihrem Kotlin-Code
DSL mit addDestination()
:
// 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 Plus-Operator verwenden, um eine neue erstelltes Ziel direkt in die Grafik ein:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
Zielargumente bereitstellen
Jedes Ziel kann Argumente definieren, die optional oder erforderlich sind. Aktionen
können mithilfe der
argument()
auf NavDestinationBuilder
, der Basisklasse für alle
Ziel-Builder-Typen. Diese Funktion verwendet den Namen des Arguments als String
und eine Lambda-Funktion,
die zum Erstellen und Konfigurieren eines
NavArgument
Innerhalb der Lambda-Funktion können Sie den Datentyp des Arguments angeben. Dies ist ein Standardwert, wenn zutreffend sind und ob Nullwerte zulässig sind.
fragment<PlantDetailFragment>("${nav_routes.plant_detail}/{${nav_arguments.plant_id}}") {
label = getString(R.string.plant_details_title)
argument(nav_arguments.plant_id) {
type = NavType.StringType
defaultValue = getString(R.string.default_plant_id)
nullable = true // default false
}
}
Wenn eine defaultValue
angegeben ist, kann der Typ abgeleitet werden. Wenn sowohl ein defaultValue
und ein type
angegeben wird, müssen die Typen übereinstimmen. Weitere Informationen finden Sie in der
NavType-Referenzdokumentation für ein
vollständige Liste der verfügbaren Argumenttypen.
Benutzerdefinierte Typen bereitstellen
Bestimmte Typen wie
ParcelableType
und
SerializableType
,
das Parsen von Werten aus Zeichenfolgen, die von Routen oder Deeplinks verwendet werden, nicht unterstützt.
Das liegt daran, dass sie nicht auf Reflexion zur Laufzeit basieren. Durch die Bereitstellung einer benutzerdefinierten
NavType
können Sie genau steuern, wie Ihr Typ geparst wird.
Deeplink zu aktivieren. So können Sie die
Kotlin-Serialisierung oder andere
Bibliotheken für reflexionsfreie Codierung und Decodierung Ihres benutzerdefinierten Typs bereitstellen.
Beispiel: Eine Datenklasse, die Suchparameter darstellt, die an Ihre
Suchbildschirm könnte sowohl Serializable
implementieren (zur Bereitstellung der
Unterstützung für Codierung/Decodierung) und Parcelize
(für Speichern und Wiederherstellen)
von einem Bundle
):
@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 Kotlin-DSL verwendet werden:
fragment<SearchFragment>(nav_routes.plant_search) {
label = getString(R.string.plant_search_title)
argument(nav_arguments.search_parameters) {
type = SearchParametersType
defaultValue = SearchParameters("cactus", emptyList())
}
}
NavType
kapselt sowohl das Schreiben als auch das Lesen jedes Felds ein, was
bedeutet, dass die NavType
auch verwendet werden muss, wenn Sie zum
Ziel, um sicherzustellen, dass die Formate übereinstimmen:
val params = SearchParameters("rose", listOf("available"))
val searchArgument = SearchParametersType.serializeAsValue(params)
navController.navigate("${nav_routes.plant_search}/$searchArgument")
Der Parameter kann aus den Argumenten im Ziel abgerufen werden:
val params: SearchParameters? = arguments?.getParcelable(nav_arguments.search_parameters)
Deeplinks
Deeplinks können jedem Ziel hinzugefügt werden, genau wie bei einem XML-gestützten Navigationsdiagramm. Alle Verfahren, die in Deeplink für ein Ziel erstellen auf den Prozess der Erstellung expliziten Deeplinks mithilfe der Methode 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 Ihrer AndroidManifest.xml
-Datei und muss stattdessen
Intent-Filter für Ihre Aktivitäten manuell
Der von Ihnen bereitgestellte Intent-Filter sollte mit dem Basis-URL-Muster, der Aktion und
MIME-Typ der Deeplinks Ihrer App.
Sie können für jeden einzelnen Deeplink eine spezifischere deeplink
angeben
Ziel mithilfe der
deepLink()
DSL-Funktion. Diese Funktion akzeptiert eine NavDeepLink
, die einen String
enthält
für das URI-Muster, ein String
für die Intent-Aktionen und ein
String
, die für den mimeType steht .
Beispiel:
deepLink {
uriPattern = "http://www.example.com/plants/"
action = "android.intent.action.MY_ACTION"
mimeType = "image/*"
}
Sie können beliebig viele Deeplinks hinzufügen. Bei jedem Anruf
deepLink()
wird ein neuer Deeplink an eine Liste angehängt, die für dieses Ziel verwaltet wird.
Ein komplexeres implizites Deeplink-Szenario, das auch Pfad und abfragebasierten Parametern:
val baseUri = "http://www.example.com/plants"
fragment<PlantDetailFragment>(nav_routes.plant_detail) {
label = getString(R.string.plant_details_title)
deepLink(navDeepLink {
uriPattern = "${baseUri}/{id}"
})
deepLink(navDeepLink {
uriPattern = "${baseUri}/{id}?name={plant_name}"
})
}
Sie können Stringinterpolation um die Definition zu vereinfachen.
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
.
Weitere Informationen
Sicherheit der Navigationstypen um zu erfahren, wie Sie Typsicherheit für Ihre Kotlin-DSL-Datei und Navigation Compose-Code.