Cómo actualizar los componentes de IU con NavigationUI

El componente de arquitectura de Navigation incluye una clase NavigationUI. Esta clase contiene métodos estáticos que administran la navegación con la barra de la app superior, el panel lateral de navegación y la navegación inferior.

Cómo detectar eventos de navegación

La interacción con NavController es el método principal para navegar entre destinos. NavController es responsable de reemplazar el contenido de NavHost con el nuevo destino. En muchos casos, los elementos de la IU (por ejemplo, una barra superior de la app o controles de navegación persistentes como BottomNavigationBar) permanecen fuera de NavHost y se deben actualizar a medida que navegas entre destinos.

NavController ofrece una interfaz de OnDestinationChangedListener a la que se llama cuando el destino actual o los argumentos de cambian. Es posible registrar un nuevo agente de escucha mediante el método addOnDestinationChangedListener(). Ten en cuenta que, al llamar a addOnDestinationChangedListener(), si existe el destino actual, se envía inmediatamente a tu objeto de escucha.

NavigationUI usa OnDestinationChangedListener para hacer que estos componentes comunes de la IU reconozcan la navegación. Sin embargo, debes tener en cuenta que también puedes usar OnDestinationChangedListener solamente para que cualquier IU personalizada o lógica de negocios esté al tanto de los eventos de navegación.

Por ejemplo, tal vez tengas elementos de IU comunes que quieras mostrar en algunas áreas de tu app y ocultar en otras. Con tu propio OnDestinationChangedListener, puedes ocultar o mostrar selectivamente estos elementos de la IU en función del destino objetivo, como se muestra en el siguiente ejemplo:

Kotlin

    navController.addOnDestinationChangedListener { _, destination, _ ->
       if(destination.id == R.id.full_screen_destination) {
           toolbar.visibility = View.GONE
           bottomNavigationView.visibility = View.GONE
       } else {
           toolbar.visibility = View.VISIBLE
           bottomNavigationView.visibility = View.VISIBLE
       }
    }
    

Java

    navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
       @Override
       public void onDestinationChanged(@NonNull NavController controller,
               @NonNull NavDestination destination, @Nullable Bundle arguments) {
           if(destination.getId() == R.id.full_screen_destination) {
               toolbar.setVisibility(View.GONE);
               bottomNavigationView.setVisibility(View.GONE);
           } else {
               toolbar.setVisibility(View.VISIBLE);
               bottomNavigationView.setVisibility(View.VISIBLE);
           }
       }
    });
    

Barra de la app superior

La barra de la app superior proporciona un lugar constante en la parte superior de la app para mostrar información y acciones desde la pantalla actual.

NavigationUI contiene métodos que actualizan el contenido automáticamente de la barra de la app superior a medida que los usuarios navegan por la app. Por ejemplo, usa las etiquetas de destino del gráfico de navegación para mantener actualizado el título de la barra de la app superior.

Cuando se utiliza NavigationUI con los métodos de la barra de la app superior que se analizan a continuación, la etiqueta que adjuntas a los destinos se puede completar automáticamente a partir de los argumentos proporcionados al destino mediante el formato de {argName} en tu etiqueta.

NavigationUI proporciona compatibilidad con los siguientes tipos de barras de la app superiores:

AppBarConfiguration

NavigationUI usa un objeto AppBarConfiguration para administrar el comportamiento del botón Navigation en la esquina superior izquierda del área de visualización de tu app. De forma predeterminada, el botón Navigation está oculto cuando un usuario se encuentra en un destino de nivel superior de un gráfico de navegación y aparece como un botón Arriba en cualquier otro destino.

Para utilizar el destino de inicio de tu gráfico de navegación como el único destino de nivel superior, puedes crear un objeto AppBarConfiguration y pasar el gráfico de navegación correspondiente, como se muestra a continuación:

Kotlin

    val appBarConfiguration = AppBarConfiguration(navController.graph)
    

Java

    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph()).build();
    

Si deseas personalizar qué destinos se consideran destinos de nivel superior, puedes pasar un conjunto de ID de destino al constructor, como se muestra a continuación:

Kotlin

    val appBarConfiguration = AppBarConfiguration(setOf(R.id.main, R.id.android))
    

Java

    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(R.id.main, R.id.android).build();
    

Cómo crear una Barra Google

Para crear una Barra Google con NavigationUI, primero define la barra en tu actividad principal, como se muestra a continuación:

    <LinearLayout>
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar" />
        <fragment
            android:id="@+id/nav_host_fragment"
            ... />
        ...
    </LinearLayout>
    

Luego, llama a setupWithNavController() desde el método onCreate() de tu actividad principal, como se muestra a continuación:

Kotlin

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

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        val appBarConfiguration = AppBarConfiguration(navController.graph)
        findViewById<Toolbar>(R.id.toolbar)
            .setupWithNavController(navController, appBarConfiguration)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        AppBarConfiguration appBarConfiguration =
                new AppBarConfiguration.Builder(navController.getGraph()).build();
        Toolbar toolbar = findViewById(R.id.toolbar);
        NavigationUI.setupWithNavController(toolbar, navController);
    }
    

Cómo incluir CollapsingToolbarLayout

Para incluir CollapsingToolbarLayout con tu Barra Google, primero define la Barra Google y el diseño circundante en tu actividad principal, como se muestra a continuación:

    <LinearLayout>
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/tall_toolbar_height">

            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:contentScrim="?attr/colorPrimary"
                app:expandedTitleGravity="top"
                app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:layout_collapseMode="pin"/>
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>

        <fragment
            android:id="@+id/nav_host_fragment"
            ... />
        ...
    </LinearLayout>
    

Luego, llama a setupWithNavController() desde el método onCreate de tu actividad principal, como se muestra a continuación:

Kotlin

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

        ...

        val layout = findViewById<CollapsingToolbarLayout>(R.id.collapsing_toolbar_layout)
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        val navController = findNavController(R.id.nav_host_fragment)
        val appBarConfiguration = AppBarConfiguration(navController.graph)
        layout.setupWithNavController(toolbar, navController, appBarConfiguration)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        CollapsingToolbarLayout layout = findViewById(R.id.collapsing_toolbar_layout);
        Toolbar toolbar = findViewById(R.id.toolbar);
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        AppBarConfiguration appBarConfiguration =
                new AppBarConfiguration.Builder(navController.getGraph()).build();
        NavigationUI.setupWithNavController(layout, toolbar, navController, appBarConfiguration);
    }
    

Barra de acciones

Para agregar compatibilidad de navegación a la barra de acciones predeterminada, llama a setupActionBarWithNavController() desde el método onCreate() de tu actividad principal, como se muestra a continuación. Ten en cuenta que debes declarar tu AppBarConfiguration fuera de onCreate(), ya que también lo usas cuando anulas onSupportNavigateUp():

Kotlin

    private lateinit var appBarConfiguration: AppBarConfiguration

    ...

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        val navController = findNavController(R.id.nav_host_fragment)
        appBarConfiguration = AppBarConfiguration(navController.graph)
        setupActionBarWithNavController(navController, appBarConfiguration)
    }
    

Java

    AppBarConfiguration appBarConfiguration;

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
    }
    

Luego, anula onSupportNavigateUp() para manejar la navegación hacia arriba:

Kotlin

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

Java

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, appBarConfiguration)
                || super.onSupportNavigateUp();
    }
    

Cómo vincular destinos a los elementos de menú

NavigationUI también proporciona asistentes para vincular destinos a componentes de IU controlados por el menú. NavigationUI contiene un método asistente, onNavDestinationSelected(), que toma un elemento MenuItem junto con el elemento NavController que aloja el destino asociado. Si el elemento id de MenuItem coincide con el elemento id del destino, NavController puede navegar a ese destino.

A modo de ejemplo, los siguientes fragmentos XML definen un elemento de menú y un destino con un id común, details_page_fragment:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        ... >

        ...

        <fragment android:id="@+id/details_page_fragment"
             android:label="@string/details"
             android:name="com.example.android.myapp.DetailsFragment" />
    </navigation>
    
    <menu xmlns:android="http://schemas.android.com/apk/res/android">

        ...

        <item
            android:id="@id/details_page_fragment"
            android:icon="@drawable/ic_details"
            android:title="@string/details" />
    </menu>
    

Si tu menú se agregó a través del elemento onCreateOptionsMenu() de Activity, por ejemplo, puedes asociar los elementos del menú con los destinos si anulas el elemento onOptionsItemSelected() de Activity para llamar a onNavDestinationSelected(), como se muestra a continuación:

Kotlin

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item)
    }
    

Java

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.onNavDestinationSelected(item, navController)
                || super.onOptionsItemSelected(item);
    }
    

Ahora, cuando un usuario hace clic en el elemento del menú details_page_fragment, la app navega automáticamente al destino correspondiente con el mismo id.

Cómo agregar un panel lateral de navegación

El panel lateral de navegación es un panel de IU que muestra el menú de navegación principal de tu app. El panel lateral aparece cuando el usuario toca el ícono del panel lateral en la barra de la app o cuando el usuario desliza un dedo desde el borde izquierdo de la pantalla.

El ícono del panel lateral se muestra en todos los destinos de nivel superior que usan un DrawerLayout. Los destinos de nivel superior son los destinos de nivel raíz de tu app. No muestran un botón Arriba en la barra de la app.

Para agregar un panel lateral de navegación, primero debes declarar un DrawerLayout como vista raíz. Dentro del DrawerLayout, agrega un diseño para el contenido principal de la IU y otra vista que tenga el contenido del panel lateral de navegación.

Por ejemplo, en el siguiente diseño, se utiliza un DrawerLayout con dos vistas secundarias: un NavHostFragment para contener el contenido principal y un NavigationView para el contenido del panel lateral de navegación.

<?xml version="1.0" encoding="utf-8"?>
    <!-- Use DrawerLayout as root container for activity -->
    <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <!-- Layout to contain contents of main body of screen (drawer will slide over this) -->
        <fragment
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:id="@+id/nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

        <!-- Container for contents of drawer - use NavigationView to make configuration easier -->
        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true" />

    </android.support.v4.widget.DrawerLayout>
    

Luego, conecta el DrawerLayout a tu gráfico de navegación. Para ello, pásalo a AppBarConfiguration, como se muestra a continuación:

Kotlin

    val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
    

Java

    AppBarConfiguration appBarConfiguration =
            new AppBarConfiguration.Builder(navController.getGraph())
                .setDrawerLayout(drawerLayout)
                .build();
    

Luego, en tu clase de actividad principal, llama a setupWithNavController() desde el método onCreate() de tu actividad principal, como se muestra a continuación:

Kotlin

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

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        findViewById<NavigationView>(R.id.nav_view)
            .setupWithNavController(navController)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationView navView = findViewById(R.id.nav_view);
        NavigationUI.setupWithNavController(navView, navController);
    }
    

Navegación inferior

NavigationUI también puede controlar la navegación inferior. Cuando un usuario selecciona un elemento del menú, NavController llama a onNavDestinationSelected() y actualiza automáticamente el elemento seleccionado en la barra de navegación inferior.

Para crear una barra de navegación inferior en tu app, primero define la barra en tu actividad principal, como se muestra a continuación:

    <LinearLayout>
        ...
        <fragment
            android:id="@+id/nav_host_fragment"
            ... />
        <android.support.design.widget.BottomNavigationView
            android:id="@+id/bottom_nav"
            app:menu="@menu/menu_bottom_nav" />
    </LinearLayout>
    

Luego, en tu clase de actividad principal, llama a setupWithNavController() desde el método onCreate() de tu actividad principal, como se muestra a continuación:

Kotlin

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

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        findViewById<BottomNavigationView>(R.id.bottom_nav)
            .setupWithNavController(navController)
    }
    

Java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        ...

        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
        NavigationUI.setupWithNavController(bottomNav, navController);
    }
    

Para obtener un ejemplo completo que incluya la navegación inferior, consulta el Ejemplo de navegación avanzada de los componentes de la arquitectura de Android en GitHub.

Recursos adicionales

Para obtener más información acerca de la navegación, consulta los siguientes recursos adicionales.

Ejemplos

Codelabs

Publicaciones de blog

Videos