Únete a ⁠ #Android11: The Beta Launch Show el 3 de junio.

Cómo navegar a un destino

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 más adelante en las secciones a continuación.

Si deseas recuperar el objeto NavController de un fragmento, una actividad o una vista, usa uno de los siguientes métodos.

Kotlin:

Java:

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 secciones a continuación.

Cómo usar Safe Args para navegar con seguridad de tipo

Para navegar entre destinos, se recomienda usar el complemento de Gradle de Safe Args. Este complemento 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 para pasar datos entre destinos.

Para agregar Safe Args a tu proyecto, incluye la siguiente classpath en tu archivo build.gradle de nivel superior:

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.3.0-alpha06"
        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:

apply plugin: "androidx.navigation.safeargs"

Como alternativa, para generar el código de Kotlin adecuado para módulos exclusivos de Kotlin, agrega lo siguiente:

apply plugin: "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 habilitar Safe Args, tu código generado contiene clases y métodos para cada acción que definiste, además de clases que corresponden 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. Este método 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 muestra en el ejemplo a continuación:

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);
    }
    

Para obtener más información sobre cómo 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 fin 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.

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: El ID de recurso del destino objetivo
  • Argumentos predeterminados: Un android.os.Bundle que contiene valores predeterminados para el destino objetivo, si se lo suministró
  • Opciones de navegación: Las opciones de navegación, 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 se infla el gráfico de navegación, se analizan estas acciones y los objetos NavAction correspondientes se generan 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 estas 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

    Navigation.findNavController(this).navigate(R.id.action_b_to_a);
    

Cómo navegar con URI

Puedes usar navigate(Uri) para navegar directamente a un destino de vínculo directo implícito, como se muestra en el siguiente ejemplo:

Kotlin

    val navController = findNavController()
    val deeplink = Uri.parse("android-app://androidx.navigation.app/profile")
    findNavController().navigate(deeplink)
    

Java

    Uri deeplink = Uri.parse("android-app://androidx.navigation.app/profile");
    view.findNavController().navigate(deeplink);
    

A diferencia de la navegación mediante ID de acción o destino, puedes navegar a cualquier URI en tu gráfico, independientemente de que el destino esté visible o no. Puedes navegar a un destino en el gráfico actual o a un destino en un gráfico completamente diferente.

Cuando navegas con URI, no se restablece la pila de actividades. Es diferente a los otros métodos de navegación de vínculo directo, en los que se reemplaza la pila de actividades durante la navegación. Sin embargo, 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 la app, se coloca el primer destino de tu 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ó correctamente. El caso más común en el que 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 administrar 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). Para 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 (se muestra como emergente) 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.