Implementar El Navigation Component

En este tutorial te mostraremos los pasos para implementar el Navigation Component en tus Apps Android. Así tendrás una arquitectura de navegación basada en fragmentos.

1. Crea Un Proyecto Nuevo

Entra a Android Studio y crea un nuevo proyecto Kotlin llamado «Navigation Component».

Usa como plantilla una Empty Activity para que te sirva como la actividad de punto de entrada.

Omite este paso si vas a usar un proyecto existente, donde deseas añadir el componente.

2. Añadir Dependencias Del Navigation Component

Abre tú archivo build.gradle a nivel de proyecto y escribe una variable para la versión actual del navigation component:

ext{
    nav_version = "2.3.2"
}

Ahora ve a build.gradle del módulo y añade las siguientes dependencias para los componentes de navegación:

dependencies {

    /*...*/

    // Navegación
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

    /*...*/

}

Luego sincroniza tu proyecto para incorporar los paquetes.

3. Crear Gráfico De Navegación

Como siguiente paso, añade el navigation graph a los recursos de tu proyecto.

Haz click derecho en la carpeta res y seleccionar New|Android Resource File.

Cuando se despliegue el diálogo de creación, usa para File Name el nombre «nav_graph» y elige la entrada Navigation en Resource Type. Confirma la creación con OK.

Crear navigation graph

Luego podrás ver al archivo en el directorio res/navigation.

Navigation graph en directorio res

4. Crear NavHostFragment

Recordemos que NavHostFragment provee un área dentro de tu layout, en donde ocurrirá la navegación.

Para su creación, abre el layout creado por defecto por Android Studio llamado activity_main.xml.

Crea una etiqueta <FragmentContainerView> en su interior y define los siguientes tres atributos relacionados a la navegación:

  • android:name — Usa NavHostFragment como clase de creación
  • app:navGraph — Vincula al recurso nav_graph.xml
  • app:defaultNavHost — Usa true para otorga el control del botón «Atrás» del sistema a este NavHostFragment.

El código final sería este:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

5. Añadir Fragmentos Al Gráfico De Navegación

Para este ejemplo vamos a crear tres fragmentos para simular:

  • Una pantalla de lista
  • Un pantalla de detalle
  • Una pantalla de creación y edición.

Cada uno de estos elementos representará un destino (destination) de la navegación.

Abre nav_graph.xml para visualizar el editor de navegación. Si no lo ves, presiona la pestaña Split o Design del recurso.

Crea el primer fragmento de lista, presionando el botón New Destination y luego Create new destination:

Crear nuevo destino en navigation graph

Estas acciones desplegaran un diálogo para crear fragmentos, donde seleccionarás el tipo de plantilla que requieras. En este ejemplo usaremos Fragment (Blank) por simplicidad.

Nombra al primer fragmento como «ExampleListFragment» y confirma su creación. Verás que en el editor de navegación este se reflejará como el primer destino.

Destino en editor de navegación

Ahora, repite el mismo proceso con los destinos de detalle y creación/edición hasta que tu editor se vea así:

6. Elegir Destino Inicial

En este momento ExampleListFragment es el destino inicial, lo cual es lo que deseamos.

Pero si deseas modificarlo, haz click derecho en el destino y luego selecciona Set as Start Destination:

Set as Start Destination

Esto es equivalente a modificar el atributo app:startDestination del elemento <navigation> en nav_graph.xml. Usa el id del fragmento que deseas establecer como inicio.

<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/exampleListFragment">

    <!-- ... ->
</navigation>

7. Conectar Destinos

La conexión entre dos destinos es representada por una Acción en el editor o <action> en el código.

Para conectar dos destinos en el editor, sitúate en el destino de origen y arrastra el circulo emergente en su borde hacia el destino final.

Conectar destinos de navegación

Para nuestro ejemplo necesitaremos las siguientes conexiones:

  • De Lista a detalle
  • De detalle a edición
  • De Lista a creación

Al generar las acciones deberías ver lo siguiente en el editor:

Acciones de navegación

Si cambias a la pestaña Code tendrás la siguiente definición XML:

<?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/exampleListFragment">

    <fragment
        android:id="@+id/exampleListFragment"
        android:name="com.develou.navigationcomponent.ExampleListFragment"
        android:label="fragment_example_list"
        tools:layout="@layout/fragment_example_list" >
        <action
            android:id="@+id/action_exampleListFragment_to_exampleDetailFragment3"
            app:destination="@id/exampleDetailFragment" />
        <action
            android:id="@+id/action_exampleListFragment_to_exampleAddEditFragment"
            app:destination="@id/exampleAddEditFragment" />
    </fragment>
    <fragment
        android:id="@+id/exampleDetailFragment"
        android:name="com.develou.navigationcomponent.ExampleDetailFragment"
        android:label="fragment_example_detail"
        tools:layout="@layout/fragment_example_detail" >
        <action
            android:id="@+id/action_exampleDetailFragment_to_exampleAddEditFragment2"
            app:destination="@id/exampleAddEditFragment" />
    </fragment>
    <fragment
        android:id="@+id/exampleAddEditFragment"
        android:name="com.develou.navigationcomponent.ExampleAddEditFragment"
        android:label="fragment_example_add_edit"
        tools:layout="@layout/fragment_example_add_edit" />
</navigation>

Para mejorar la legibilidad de los ids de las acciones, refactorizalos a:

  • action_list_to_detail
  • action_list_to_add
  • action_detail_to_edit

8. Navegar Hacia Un Destino

El elemento encargado de realizar la navegación entre fragmentos es el NavController. Este recibe la acción generada en el editor de navegación para iniciar al destino establecido.

Navegar De Lista A Detalle

Para iniciar la navegación necesitamos controles de interfaz que permitan desencadenar las acciones.

Modifica el layout de ExampleListFragment para que posea dos botones. Uno para ir hacia el detalle y otro para la creación.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".ExampleListFragment">

    <Button
        android:id="@+id/detail_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="DETALLE" />

    <Button
        android:id="@+id/create_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CREAR" />

</LinearLayout>
Controles para navegación en lista

Con los botones en el layout, ahora ve a ExampleListFragment.kt y:

  • Obtén las referencias de los botones
  • Asignales una escucha de click
  • Encuentra al NavController con View.findNavController()
  • Usa el método navigate(), pasando como parámetro el id de las acciones de navegación

El fragmento de lista tendría este código al aplicar las acciones anteriores:

class ExampleListFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflar layout
        val root = inflater.inflate(R.layout.fragment_example_list, container, false)
        setupNavigation(root)
        return root
    }

    private fun setupNavigation(root: View) {
        val detailButton = root.findViewById<Button>(R.id.detail_button)
        val createButton = root.findViewById<Button>(R.id.create_button)
        detailButton.setOnClickListener { view: View ->
            view.findNavController().navigate(R.id.action_list_to_detail)
        }
        createButton.setOnClickListener { view: View ->
            view.findNavController().navigate(R.id.action_list_to_add)
        }
    }
}

Repite este proceso para ExampleDetailFragment. Añádele un botón que vaya hacia la edición y llama a navigate() en el evento de click.

9. Añadir Up Button En La App Bar

Lo siguiente es añadir el Up Button en la barra de acción.

Up Button con NavController

Abre MainActivity, ve a onCreate() y obtén la referencia del NavHostFragment.

Luego llama método NavigationUI.setupActionBarWithNavController() para que la App Bar sea usada por NavContoller:

override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     val navHostFragment =
         supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
     NavigationUI.setupActionBarWithNavController(this, navHostFragment.navController)
}

Luego sobrescribe onSupportNavigateUp() para transferir la responsabilidad del evento del Up Button al NavController.

Esto se logra llamando a NavController.navigateUp():

override fun onSupportNavigateUp(): Boolean {
     val navController = this.findNavController(R.id.nav_host_fragment)
     return navController.navigateUp()
}

Modificar Título De App Bar

Si corres la aplicación en el estado actual verás los labels autogenerados en el título de la App Bar. Esto se debe a que el NavController toma el atibuto android:label de los fragmentos de nav_graph.xml.

Evidentemente, tener los títulos correctos consiste en usar los labels correctos. Por lo que es solo cuestión de modificarlos:

Por ejemplo, al fragmento para la creación/edición puedes ponerle ese mismo título:

<fragment
    android:id="@+id/exampleAddEditFragment"
    android:name="com.develou.navigationcomponent.ExampleAddEditFragment"
    android:label="Creación/Edición"
    tools:layout="@layout/fragment_example_add_edit" />

Nota: Reemplaza los strings escritos directamente por recursos.

10. Correr Aplicación Android

Finalmente ejecuta el proyecto y navega entre los diferentes destinos:

Implementar El Navigation Component

Puedes descargar el código desde el siguiente enlace:

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!