La navegación a un destino se realiza con un NavController
, un objeto que administra la navegación de una app dentro de un elemento NavHost
. Cada NavHost
tiene su propio NavController
correspondiente. NavController
ofrece algunas maneras de navegar a un destino, que se describen en mayor detalle en las siguientes secciones.
Si deseas recuperar el objeto NavController
de un fragmento, una actividad o una vista, usa uno de los siguientes métodos:
Kotlin:
Java:
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)
Después de recuperar un objeto NavController
, puedes llamar a una de las sobrecargas de navigate()
para navegar entre los destinos. Cada sobrecarga ofrece compatibilidad con varios casos de navegación, como se describe en las siguientes secciones.
Cómo usar Safe Args para navegar con seguridad de tipo
Para navegar entre destinos, te recomendamos que uses el complemento de Gradle Safe Args, que genera clases simples de objeto y compilador que permiten navegar con seguridad de tipo entre destinos. Se recomienda el uso de Safe Args para navegar y pasar datos entre destinos.
Para agregar Safe Args a tu proyecto, incluye la siguiente classpath
en tu archivo build.gradle
de nivel superior:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.5.3" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.5.3" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
También debes aplicar uno de los dos complementos disponibles.
Para generar código de lenguaje Java adecuado para Java o módulos combinados de Java y Kotlin, agrega esta línea al archivo build.gradle
de tu app o módulo:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Como alternativa, para generar el código de Kotlin adecuado para módulos solo de Kotlin, agrega lo siguiente:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
Tienes que tener el objeto android.useAndroidX=true
en tu archivo gradle.properties
, según se indica en Cómo migrar a AndroidX.
Después de que habilites Safe Args, tu código generado contendrá clases y métodos para cada acción que hayas definido, además de clases que correspondan a cada destino de envío y recepción.
Safe Args genera una clase para cada destino en el que se origina una acción. Al nombre de la clase de destino de origen se le agrega "Directions". Por ejemplo, si el destino de origen tiene el nombre SpecifyAmountFragment
, la clase generada se llamará SpecifyAmountFragmentDirections
.
La clase generada contiene un método estático para cada acción definida en el destino de origen, que toma cualquier parámetro de acción definido como argumento y muestra un objeto NavDirections
que puedes pasar directamente a navigate()
.
Ejemplo de Safe Args
Como ejemplo, imagina que tienes un gráfico de navegación con una sola acción que conecta dos destinos: SpecifyAmountFragment
y ConfirmationFragment
.
El objeto ConfirmationFragment
toma un solo parámetro float
que proporcionas como parte de la acción.
Safe Args genera una clase SpecifyAmountFragmentDirections
con un solo método, actionSpecifyAmountFragmentToConfirmationFragment()
y una clase interna llamada ActionSpecifyAmountFragmentToConfirmationFragment
. La clase interna deriva de NavDirections
y almacena el ID de acción asociado y el parámetro float
. Luego, el objeto NavDirections
que se muestra se puede pasar directamente a navigate()
, como se ve en el siguiente ejemplo:
Kotlin
override fun onClick(v: View) { val amount: Float = ... val action = SpecifyAmountFragmentDirections .actionSpecifyAmountFragmentToConfirmationFragment(amount) v.findNavController().navigate(action) }
Java
@Override public void onClick(View view) { float amount = ...; action = SpecifyAmountFragmentDirections .actionSpecifyAmountFragmentToConfirmationFragment(amount); Navigation.findNavController(view).navigate(action); }
A fin de obtener más información para pasar datos entre destinos con Safe Args, consulta Cómo usar Safe Args para pasar datos con seguridad de tipo.
Cómo navegar con ID
El objeto navigate(int)
toma el ID de recurso de una acción o un destino. En el siguiente fragmento de código, se muestra cómo navegar al elemento ViewTransactionsFragment
:
Kotlin
viewTransactionsButton.setOnClickListener { view -> view.findNavController().navigate(R.id.viewTransactionsAction) }
Java
viewTransactionsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Navigation.findNavController(view).navigate(R.id.viewTransactionsAction); } });
Para los botones, también puedes usar el método de conveniencia createNavigateOnClickListener()
de la clase Navigation
a los efectos de navegar a un destino, como se muestra en el ejemplo a continuación:
Kotlin
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))
Java
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));
Para controlar otros componentes de IU comunes, como la barra de la app superior y la navegación inferior, consulta Cómo actualizar componentes de IU con NavigationUI.
Cómo proporcionar opciones de navegación a las acciones
Cuando defines una acción en el gráfico de navegación, Navigation genera una clase NavAction
correspondiente, que contiene las configuraciones definidas para esa acción, incluidas las siguientes:
- Destino: es el ID de recurso del destino objetivo.
- Argumentos predeterminados: es un
android.os.Bundle
que contiene valores predeterminados para el destino objetivo, si se lo suministró. - Opciones de navegación: son las opciones de Navigation, representadas como
NavOptions
. Esta clase contiene toda la configuración especial para la transición hacia el destino objetivo y desde él, lo que incluye la configuración del recurso de animación, el comportamiento emergente y si el destino debería lanzarse en un solo modo superior.
Veamos un ejemplo de un gráfico con dos pantallas y una acción para navegar de una a la otra:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/a">
<fragment android:id="@+id/a"
android:name="com.example.myapplication.FragmentA"
android:label="a"
tools:layout="@layout/a">
<action android:id="@+id/action_a_to_b"
app:destination="@id/b"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment android:id="@+id/b"
android:name="com.example.myapplication.FragmentB"
android:label="b"
tools:layout="@layout/b">
<action android:id="@+id/action_b_to_a"
app:destination="@id/a"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
Cuando aumenta el gráfico de navegación, se analizan esas acciones y se generan los objetos NavAction
correspondientes con las configuraciones definidas en el gráfico. Por ejemplo, se define action_b_to_a
como la navegación desde el destino b
hacia el destino a
. La acción incluye animaciones, además del comportamiento popTo
, que quita todos los destinos de la pila de actividades. Todas esas configuraciones se capturan como NavOptions
y se adjuntan al NavAction
.
Para seguir esta NavAction
, usa NavController.navigate()
y pasa el ID de la acción, como se muestra en el siguiente ejemplo:
Kotlin
findNavController().navigate(R.id.action_b_to_a)
Java
NavigationHostFragment.findNavController(this).navigate(R.id.action_b_to_a);
Cómo navegar con DeepLinkRequest
Puedes usar navigate(NavDeepLinkRequest)
para navegar directamente a un destino de vínculo directo implícito, como se muestra en el siguiente ejemplo:
Kotlin
val request = NavDeepLinkRequest.Builder .fromUri("android-app://androidx.navigation.app/profile".toUri()) .build() findNavController().navigate(request)
Java
NavDeepLinkRequest request = NavDeepLinkRequest.Builder .fromUri(Uri.parse("android-app://androidx.navigation.app/profile")) .build() NavHostFragment.findNavController(this).navigate(request)
Además de Uri
, NavDeepLinkRequest
también admite vínculos directos con acciones y tipos de MIME. Para agregar una acción a la solicitud, usa fromAction()
o setAction()
.
Para agregar un tipo de MIME a una solicitud, usa fromMimeType()
o setMimeType()
.
A los efectos de que una NavDeepLinkRequest
coincida de forma adecuada con un destino de vínculo directo implícito, el URI, la acción y el tipo de MIME deben coincidir con el NavDeepLink
en el destino. Los URI deben coincidir con el patrón, las acciones deben tener una concordancia exacta, y los tipos de MIME deben estar relacionados (p. ej., "image/jpg" coincide con "image/*").
A diferencia de la navegación mediante ID de acción o destino, puedes navegar a cualquier vínculo directo en tu gráfico, ya sea que el destino esté visible o no. Puedes navegar a un destino en el gráfico actual o a uno en un gráfico completamente diferente.
Cuando navegas con NavDeepLinkRequest
, la pila de actividades no se restablece. Este comportamiento difiere de otros métodos de navegación de vínculo directo, en los que se reemplaza la pila de actividades durante la navegación. Los objetos popUpTo
y popUpToInclusive
todavía quitan destinos de la pila de actividades como si hubieses navegado mediante un ID.
Navigation y la pila de actividades
Android mantiene una pila de actividades que incluye los destinos que visitaste. Cuando el usuario abre tu app, se coloca el primer destino de la app en la pila. Cada llamada al método navigate()
ubica otro destino sobre la pila. Cuando presionas Up o Back, se llama a los métodos NavController.navigateUp()
y NavController.popBackStack()
, respectivamente, para quitar (o hacer que aparezca como emergente) el destino superior de la pila.
NavController.popBackStack()
muestra un valor booleano que indica si otro destino apareció con éxito. El caso más común en el que se muestra false
es cuando resaltas de forma manual el destino de inicio de tu gráfico.
Cuando el método muestra false
, el objeto NavController.getCurrentDestination()
muestra null
. Eres responsable de navegar a un destino nuevo o de controlar el elemento emergente. Para ello, llama a finish()
en tu actividad, como se muestra en el siguiente ejemplo:
Kotlin
... if (!navController.popBackStack()) { // Call finish() on your Activity finish() }
Java
... if (!navController.popBackStack()) { // Call finish() on your Activity finish(); }
popUpTo y popUpToInclusive
Cuando navegas a través de una acción, puedes elegir resaltar destinos adicionales de la pila de actividades. Por ejemplo, si tu app tiene un flujo de acceso inicial, una vez que un usuario accede, debes resaltar todos los destinos relacionados con el acceso en la pila de actividades para que el botón Atrás no vuelva a dirigir a los usuarios al flujo de acceso.
Para mostrar los destinos cuando navegas de un destino al otro, agrega un atributo app:popUpTo
al elemento <action>
asociado. El atributo app:popUpTo
le indica a la biblioteca de Navigation que resalte algunos destinos de la pila de actividades como parte de la llamada a navigate()
. El valor de atributo es el ID del destino más reciente que debe permanecer en la pila.
Además, puedes incluir app:popUpToInclusive="true"
para indicar que el destino especificado en el atributo app:popUpTo
se debería quitar también de la pila de actividades.
Ejemplo de popUpTo: lógica circular
Supongamos que tu app tiene tres destinos (A, B y C) junto con las acciones que conducen de a A a B, de B a C y de C nuevamente a A. En la Figura 1, se muestra el gráfico de navegación correspondiente:
Figura 1: Un gráfico de navegación circular con tres destinos: A, B y C.
Con cada acción de navegación, se agrega un destino a la pila de actividades. Si navegas varias veces por este flujo, tu pila de actividades contendrá varios conjuntos de cada destino (A, B, C, A, B, C, A y así sucesivamente).
A fin de evitar esta repetición, puedes especificar los atributos app:popUpTo
y app:popUpToInclusive
en la acción que te dirige del destino C al destino A, como se muestra en el siguiente ejemplo:
<fragment android:id="@+id/c" android:name="com.example.myapplication.C" android:label="fragment_c" tools:layout="@layout/fragment_c"> <action android:id="@+id/action_c_to_a" app:destination="@id/a" app:popUpTo="@+id/a" app:popUpToInclusive="true"/> </fragment>
Después de llegar al destino C, la pila de actividades contiene una instancia de cada destino (A, B, C). Cuando navegas de vuelta al destino A, también popUpTo
A, lo que significa que quitamos B y C de la pila durante la navegación. También usamos app:popUpToInclusive="true"
para destacar la primera A de la pila y la borramos efectivamente. Ten en cuenta que si no usas app:popUpToInclusive
, tu pila de actividades contendrá dos instancias del destino A.