El componente Navigation ofrece un lenguaje específico de dominio basado en Kotlin.
que se basa en la API de tipo seguro de Kotlin
compiladores
de Google Cloud. Esta API te permite componer de forma declarativa el gráfico en el código Kotlin, en lugar
dentro de un recurso XML. Esto puede ser útil si deseas compilar la infraestructura
la navegación de forma dinámica. Por ejemplo, tu app puede descargar y almacenar en caché
en la configuración de navegación de un servicio web externo
para compilar dinámicamente un gráfico de navegación en el directorio
función onCreate()
.
Dependencias
Para usar la DSL de Kotlin con fragmentos, agrega la siguiente dependencia al archivo
Archivo build.gradle
:
Groovy
dependencies { def nav_version = "2.8.0" api "androidx.navigation:navigation-fragment-ktx:$nav_version" }
Kotlin
dependencies { val nav_version = "2.8.0" api("androidx.navigation:navigation-fragment-ktx:$nav_version") }
Cómo compilar un gráfico
Este es un ejemplo básico basado en el llamado Sunflower
de la app. Para este
ejemplo, tenemos dos destinos: home
y plant_detail
. El destino home
está presente cuando el usuario inicia la app por primera vez. Este destino muestra una lista de plantas del jardín del usuario. Cuando el usuario selecciona una de las plantas, la app navega hacia el destino plant_detail
.
En la figura 1, se muestran estos destinos junto con los argumentos que requiere el destino plant_detail
y una acción, to_plant_detail
, que la app usa para navegar del objeto home
a plant_detail
.
Cómo alojar un gráfico de navegación DSL de Kotlin
Antes de crear el gráfico de navegación de tu app, necesitas un lugar donde alojar
gráfico. En este ejemplo, se usan fragmentos, por lo que se aloja el gráfico en un NavHostFragment
dentro de una 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>
Ten en cuenta que el atributo app:navGraph
no se establece en este ejemplo. El gráfico
no se define como un recurso en
la carpeta res/navigation
, por lo que debe configurarse como parte de onCreate()
proceso en la actividad.
En XML, una acción vincula un ID de destino con uno o más argumentos. Sin embargo, cuando se usa la DSL de Navigation, una ruta puede contener argumentos como parte del la ruta. Eso significa que no hay un concepto de acciones cuando se usa la DSL.
El siguiente paso es definir las rutas que usarás cuando definas tu gráfico.
Crea rutas para tu grafo
Los gráficos de navegación basados en XML se analizan como parte
del proceso de compilación de Android. Se crea una constante numérica para cada id
definido en el gráfico. Estos IDs estáticos generados por el tiempo de compilación no son
disponibles cuando compilas tu gráfico de navegación en el tiempo de ejecución para que la DSL de Navigation
usa serializables
tipos en lugar de
de sus IDs. Cada ruta está representada por un tipo único.
Cuando se trata de argumentos, estos están integrados en la ruta del tipo de fila. Esto te permite tener seguridad de tipos para tus argumentos de navegación.
@Serializable data object Home
@Serializable data class Plant(val id: String)
Cómo compilar un gráfico con el DSL de NavGraphBuilder
Una vez que hayas definido tus rutas, podrás crear el gráfico de navegación.
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)
}
}
En este ejemplo, dos destinos de fragmentos se definen usando el
fragment()
Compilador de DSL. Esta función requiere dos tipos de
argumentos
de Google Cloud.
Primero, una clase Fragment
que proporciona la IU para este destino. Si estableces esta opción, tendrá el mismo efecto que
configurar el atributo android:name
en los destinos de fragmentos definidos
con XML.
Segundo, la ruta. Debe ser un tipo serializable que se extienda desde Any
. Integra
debe contener los argumentos de navegación que usará este destino.
y sus tipos.
La función también acepta una expresión lambda opcional para la configuración adicional, como como etiqueta de destino, así como funciones de compilador incorporadas para personalizar argumentos y vínculos directos.
Cómo navegar con tu gráfico DSL de Kotlin
Por último, puedes navegar de home
a plant_detail
con
NavController.navigate()
llamadas:
private fun navigateToPlant(plantId: String) {
findNavController().navigate(route = PlantDetail(id = plantId))
}
En PlantDetailFragment
, puedes conocer los argumentos de navegación si obtienes lo siguiente:
actual
NavBackStackEntry
y las llamadas
toRoute
para obtener la instancia de ruta.
val plantDetailRoute = findNavController().getBackStackEntry<PlantDetail>().toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
Si PlantDetailFragment
usa un ViewModel
, obtén la instancia de ruta con el siguiente comando:
SavedStateHandle.toRoute
val plantDetailRoute = savedStateHandle.toRoute<PlantDetail>()
val plantId = plantDetailRoute.id
En el resto de esta guía, se describen los elementos comunes del gráfico de navegación, los destinos y cómo usarlos a la hora de compilar el gráfico.
Destinos
La DSL de Kotlin proporciona compatibilidad integrada con tres tipos de destino: Fragment
, Activity
y NavGraph
, cada uno de los cuales tiene su propia función de extensión intercalada disponible para compilar y configurar el destino.
Destinos de fragmentos
El
fragment()
La función DSL se puede parametrizar con la clase de fragmento de la IU y la
tipo de ruta que se usa para identificar de forma única este destino, seguido de una lambda
donde puedes proporcionar una configuración adicional, como se describe en el artículo Cómo navegar
con tu sección del gráfico DSL de Kotlin.
fragment<MyFragment, MyRoute> {
label = getString(R.string.fragment_title)
// custom argument types, deepLinks
}
Destino de la actividad
El
activity()
La función DSL toma un parámetro de tipo para la ruta, pero no se parametriza para
cualquier clase de actividad de implementación. En su lugar, debes establecer un activityClass
opcional en
una lambda final. Esta flexibilidad te permite definir un destino de actividad para
una actividad que se debe iniciar usando un valor implícito
intent, cuando se crea una
la clase de actividad
no tendría sentido. Al igual que con los destinos de fragmentos, también puedes
configurar una etiqueta, argumentos personalizados y vínculos directos.
activity<MyRoute> {
label = getString(R.string.activity_title)
// custom argument types, deepLinks...
activityClass = MyActivity::class
}
Destino del gráfico de navegación
El
navigation()
La función DSL se puede usar para crear una navegación anidada
gráfico. Esta función toma un tipo
parámetro para la ruta que se asignará a este gráfico. También incluye dos argumentos:
la ruta del destino de partida del gráfico y una lambda para
configurar el grafo. Los elementos válidos incluyen otros destinos, argumentos
tipos, vínculos directos y una etiqueta descriptiva del
destino.
Esta etiqueta puede ser útil para vincular el gráfico de navegación a los componentes de la IU usando
NavigationUI
@Serializable data object HomeGraph
@Serializable data object Home
navigation<HomeGraph>(startDestination = Home) {
// label, other destinations, deep links
}
Compatibilidad con destinos personalizados
Si usas un tipo de destino nuevo
que no admita directamente la DSL de Kotlin, puedes agregar estos destinos
tu DSL de Kotlin con
addDestination()
// The NavigatorProvider is retrieved from the NavController
val customDestination = navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
addDestination(customDestination)
Como alternativa, también puedes usar el operador unario más para agregar un destino recién construido directamente al gráfico:
// The NavigatorProvider is retrieved from the NavController
+navigatorProvider[CustomNavigator::class].createDestination().apply {
route = Graph.CustomDestination.route
}
Cómo proporcionar argumentos de destino
Los argumentos de destino se pueden definir como parte de la clase de ruta. Estas pueden ser del mismo modo que lo harías para cualquier clase de Kotlin. Los argumentos necesarios son definidos como tipos no anulables y los argumentos opcionales se definen con de salida.
El mecanismo subyacente para representar rutas y sus argumentos es la cadena
basado en la nube. El uso de cadenas para modelar rutas permite almacenar el estado de navegación y
restablecidos del disco durante la configuración
cambios y los procesos iniciados por el sistema
la muerte. Por este motivo,
cada argumento de navegación debe ser serializable, es decir, debe tener un
método que convierta la representación en la memoria del valor del argumento en un
String
La serialización de Kotlin
complemento
genera automáticamente métodos de serialización para apps básicas
tipos cuando la
La anotación @Serializable
se agrega a un objeto.
@Serializable
data class MyRoute(
val id: String,
val myList: List<Int>,
val optionalArg: String? = null
)
fragment<MyFragment, MyRoute>
Cómo proporcionar tipos personalizados
Para los tipos de argumentos personalizados, deberás proporcionar una clase NavType
personalizada. Esta
te permite controlar exactamente cómo se analiza tu tipo desde una ruta o un vínculo directo.
Por ejemplo, una ruta usada para definir una pantalla de búsqueda podría contener una clase que representa los parámetros de búsqueda:
@Serializable
data class SearchRoute(val parameters: SearchParameters)
@Serializable
data class SearchParameters(
val searchQuery: String,
val filters: List<String>
)
Un NavType
personalizado se podría escribir de la siguiente manera:
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)
}
}
Luego, puedes usarlo en la DSL de Kotlin como cualquier otro tipo:
fragment<SearchFragment, SearchRoute> {
label = getString(R.string.plant_search_title)
typeMap = mapOf(typeOf<SearchParameters>() to SearchParametersType)
}
Cuando navegues al destino, crea una instancia de tu ruta:
val params = SearchParameters("rose", listOf("available"))
navController.navigate(route = SearchRoute(params))
El parámetro se puede obtener de la ruta en el destino:
val searchRoute = navController().getBackStackEntry<SearchRoute>().toRoute<SearchRoute>()
val params = searchRoute.parameters
Vínculos directos
Los vínculos directos se pueden agregar a cualquier destino, al igual que con un gráfico de navegación basado en XML. Todos los mismos procedimientos que se definen en Cómo crear un vínculo directo de un destino se aplican al proceso de crear un vínculo directo con el DSL de Kotlin.
Cuando creas un vínculo directo implícito
Sin embargo, no tienes un recurso de navegación XML que se pueda analizar para
Elementos <deepLink>
. Por lo tanto, no puedes confiar en colocar un <nav-graph>
.
en tu archivo AndroidManifest.xml
y, en su lugar, debes agregar intent
filtros a tu actividad de forma manual. El intent
el filtro que proporciones deberá coincidir con la ruta base, la acción y el tipo de MIME de
los vínculos directos de tu app.
Los vínculos directos se agregan a un destino llamando a la función deepLink
dentro de
la lambda de destino. Acepta la ruta como un tipo parametrizado y un
El parámetro basePath
para la ruta base de la URL que se usa para el vínculo directo.
También puedes agregar una acción y un tipo de MIME con el
deepLinkBuilder
lambda final.
En el siguiente ejemplo, se crea un URI de vínculo directo para el destino Home
.
@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/*"
}
}
Formato de URI
El formato del URI del vínculo directo se genera automáticamente a partir de los campos de la ruta con las siguientes reglas:
- Los parámetros obligatorios se agregan como parámetros de ruta (por ejemplo:
/{id}
). - Los parámetros con un valor predeterminado (parámetros opcionales) se agregan como query
parámetros (ejemplo:
?name={name}
) - Las colecciones se agregan como parámetros de consulta (por ejemplo:
?items={value1}&items={value2}
) - El orden de los parámetros coincide con el orden de los campos en la ruta
Por ejemplo, el siguiente tipo de ruta:
@Serializable data class PlantDetail(
val id: String,
val name: String,
val colors: List<String>,
val latinName: String? = null,
)
tiene un formato URI generado de:
basePath/{id}/{name}/?colors={color1}&colors={color2}&latinName={latinName}
No hay límite para la cantidad de vínculos directos que puedes agregar. Cada vez que llamas a deepLink()
, se agrega un vínculo directo nuevo a una lista que se mantiene para ese destino.
Limitaciones
El complemento Safe Args no es compatible con la DSL de Kotlin, ya que busca archivos de recursos XML para generar las clases Directions
y Arguments
.