El componente Navigation incluye una clase NavigationUI
. Esta clase contiene métodos estáticos que administran la navegación con la barra superior de la app, el panel lateral de navegación y la navegación inferior.
Barra superior de la app
La barra superior de la app proporciona un lugar coherente en la parte superior de la app para mostrar información y acciones de la pantalla actual.
La NavigationUI
contiene métodos que actualizan automáticamente el contenido de la barra superior de la app a medida que los usuarios navegan por la app. Por ejemplo, NavigationUI
usa las etiquetas de destino del gráfico de navegación para mantener actualizado el título de la barra superior de la app.
<navigation> <fragment ... android:label="Page title"> ... </fragment> </navigation>
Cuando utilizas NavigationUI
con las implementaciones de la barra superior de la app que se analizan a continuación, la etiqueta que adjuntas a los destinos se puede propagar automáticamente a partir de los argumentos proporcionados al destino mediante el formato de {argName}
en tu etiqueta.
La NavigationUI
proporciona compatibilidad con los siguientes tipos de barras superiores de la app:
Si deseas obtener más información sobre las barras de app, consulta Cómo configurar la barra de la app.
AppBarConfiguration
La 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. El comportamiento del botón Navigation cambia si el usuario se encuentra en un destino de nivel superior.
Un destino de nivel superior es la raíz, o el destino de nivel más alto, en un conjunto de destinos relacionados de manera jerárquica. Los destinos de nivel superior no muestran un botón Arriba en la barra superior de la app, ya que no existe un destino superior a este. De forma predeterminada, el destino de inicio de la app es el único destino de nivel superior.
Cuando el usuario se encuentra en un destino de nivel superior, el botón Navigation se convierte en un ícono de panel lateral si el destino usa un DrawerLayout
. Si el destino no usa un DrawerLayout
, el botón Navigation está oculto. Cuando el usuario se encuentra en cualquier otro destino, el botón Navigation aparece como un botón Arriba .
Si deseas configurar el botón Navigation solo con el destino de inicio como el destino de nivel superior, crea un objeto AppBarConfiguration
y pasa 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();
En algunos casos, es posible que debas definir varios destinos de nivel superior en lugar de usar el destino de inicio predeterminado. Usar una BottomNavigationView
es un caso de uso común en este contexto, donde puedes tener pantallas del mismo nivel que no están relacionadas entre sí de manera jerárquica y cada una puede tener su propio conjunto de destinos relacionados. En casos como estos, más bien 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.profile))
Java
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.main, R.id.profile).build();
Cómo crear una barra de herramientas
Si deseas crear una barra de herramientas con NavigationUI
, primero define la barra en tu actividad principal, como se muestra a continuación:
<LinearLayout> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" /> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" ... /> ... </LinearLayout>
Luego, llama a setupWithNavController()
desde el método onCreate()
de tu actividad principal, como se muestra en el siguiente ejemplo:
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, appBarConfiguration); }
Para configurar el botón Navigation para que aparezca como un botón Arriba para todos los destinos, pasa un conjunto vacío de ID de destino para tus destinos de nivel superior cuando compiles tu AppBarConfiguration
. Esto puede ser útil, por ejemplo, si tienes una segunda actividad que debe mostrar un botón Arriba en Toolbar
en todos los destinos. Esto permite al usuario volver a la actividad principal cuando no hay otros destinos en la pila de actividades. Puedes usar setFallbackOnNavigateUpListener()
para controlar el comportamiento alternativo en los casos en los que navigateUp()
no haría nada, tal como se muestra en el siguiente ejemplo:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController val appBarConfiguration = AppBarConfiguration( topLevelDestinationIds = setOf(), fallbackOnNavigateUpListener = ::onSupportNavigateUp ) findViewById<Toolbar>(R.id.toolbar) .setupWithNavController(navController, appBarConfiguration) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { ... NavHostFragment navHostFragment = (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder() .setFallbackOnNavigateUpListener(::onSupportNavigateUp) .build(); Toolbar toolbar = findViewById(R.id.toolbar); NavigationUI.setupWithNavController( toolbar, navController, appBarConfiguration); }
Cómo incluir CollapsingToolbarLayout
Si deseas incluir un CollapsingToolbarLayout
con tu barra de herramientas, primero define la barra de herramientas y el diseño circundante en tu actividad, como se muestra a continuación:
<LinearLayout> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="@dimen/tall_toolbar_height"> <com.google.android.material.appbar.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"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"/> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.fragment.app.FragmentContainerView 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 navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController 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); NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); NavigationUI.setupWithNavController(layout, toolbar, navController, appBarConfiguration); }
Barra de acciones
Si deseas 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 usarás cuando anules onSupportNavigateUp()
:
Kotlin
private lateinit var appBarConfiguration: AppBarConfiguration ... override fun onCreate(savedInstanceState: Bundle?) { ... val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) }
Java
AppBarConfiguration appBarConfiguration; ... @Override protected void onCreate(Bundle savedInstanceState) { ... NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); }
Luego, anula onSupportNavigateUp()
para controlar 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(); }
Compatibilidad con variantes de la barra de la app
Agregar la barra superior de la app a tu actividad funciona bien cuando el diseño de la barra de la app es similar para cada destino de tu app. Sin embargo, si la barra superior de la app cambia sustancialmente entre los destinos, considera quitarla de tu actividad y definirla en cada fragmento de destino.
Por ejemplo, uno de tus destinos puede usar una Toolbar
estándar, mientras que otro usa un AppBarLayout
para crear una barra de la app más compleja con pestañas, como se muestra en la figura 2.
Si deseas implementar este ejemplo en tus fragmentos de destino con NavigationUI
, primero define la barra de la app en cada uno de los diseños de fragmentos y, luego, comienza con el fragmento de destino que usa una barra de herramientas estándar:
<LinearLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
... />
...
</LinearLayout>
A continuación, define el fragmento de destino que usa una barra de la app con pestañas:
<LinearLayout>
<com.google.android.material.appbar.AppBarLayout
... />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
... />
<com.google.android.material.tabs.TabLayout
... />
</com.google.android.material.appbar.AppBarLayout>
...
</LinearLayout>
La lógica de configuración de la navegación es la misma para ambos fragmentos, excepto que debes llamar a setupWithNavController()
desde el método onViewCreated()
de cada fragmento, en lugar de inicializarlos desde la actividad:
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val navController = findNavController() val appBarConfiguration = AppBarConfiguration(navController.graph) view.findViewById<Toolbar>(R.id.toolbar) .setupWithNavController(navController, appBarConfiguration) }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { NavController navController = Navigation.findNavController(view); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); Toolbar toolbar = view.findViewById(R.id.toolbar); NavigationUI.setupWithNavController( toolbar, navController, appBarConfiguration); }
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 onCreateOptionsMenu()
de la actividad, por ejemplo, puedes asociar los elementos de menú con los destinos si anulas el parámetro onOptionsItemSelected()
de la actividad para llamar a onNavDestinationSelected()
, como se muestra en el siguiente ejemplo:
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 haga clic en el elemento de menú details_page_fragment
, la app navegará automáticamente al destino correspondiente con el mismo elemento 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
.
Si deseas 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
que abarca 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 -->
<androidx.drawerlayout.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) -->
<androidx.fragment.app.FragmentContainerView
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 -->
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true" />
</androidx.drawerlayout.widget.DrawerLayout>
Luego, conecta el DrawerLayout
a tu gráfico de navegación. Con ese fin, pásalo a AppBarConfiguration
, como se muestra en el siguiente ejemplo:
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 navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController findViewById<NavigationView>(R.id.nav_view) .setupWithNavController(navController) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); ... NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); NavigationView navView = findViewById(R.id.nav_view); NavigationUI.setupWithNavController(navView, navController); }
A partir de Navigation 2.4.0-alpha01, se guarda y se restablece el estado de cada elemento del menú cuando usas setupWithNavController
.
Navegación inferior
NavigationUI
también puede controlar la navegación inferior. Cuando un usuario selecciona un elemento de menú, NavController
llama a onNavDestinationSelected()
y actualiza automáticamente el elemento seleccionado en la barra de navegación inferior.
Si deseas 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> ... <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" ... /> <com.google.android.material.bottomnavigation.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 navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController findViewById<BottomNavigationView>(R.id.bottom_nav) .setupWithNavController(navController) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); ... NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); BottomNavigationView bottomNav = findViewById(R.id.bottom_nav); NavigationUI.setupWithNavController(bottomNav, navController); }
A partir de Navigation 2.4.0-alpha01, se guarda y se restablece el estado de cada elemento del menú cuando usas setupWithNavController
.
Si deseas 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.
Cómo detectar eventos de navegación
La interacción con NavController
es el método principal para navegar entre destinos. El NavController
es responsable de reemplazar el contenido de NavHost
con el destino nuevo. En muchos casos, los elementos de la IU (por ejemplo, una barra superior de la app o los controles de navegación persistentes como BottomNavigationBar
) permanecen fuera del NavHost
y se deben actualizar a medida que navegas entre destinos.
El NavController
ofrece una interfaz de OnDestinationChangedListener
que se llama cuando el destino actual o los argumentos de NavController
cambian. Es posible registrar un nuevo objeto de escucha mediante el método addOnDestinationChangedListener()
. Ten en cuenta que, cuando se llama a addOnDestinationChangedListener()
, si el destino actual existe, se envía de inmediato al objeto de escucha.
La 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 el elemento OnDestinationChangedListener
solo con el fin de 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); } } });
Objetos de escucha basados en argumentos
Como alternativa, también puedes usar argumentos con valores predeterminados dentro del gráfico de navegación, que el controlador de IU adecuado puede usar para actualizar su estado. Por ejemplo, en lugar de basar la lógica en OnDestinationChangedListener
en el ID de destino según el ejemplo anterior, podemos crear un argumento en NavGraph
:
<?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" android:id="@+id/navigation\_graph" app:startDestination="@id/fragmentOne"> <fragment android:id="@+id/fragmentOne" android:name="com.example.android.navigation.FragmentOne" android:label="FragmentOne"> <action android:id="@+id/action\_fragmentOne\_to\_fragmentTwo" app:destination="@id/fragmentTwo" /> </fragment> <fragment android:id="@+id/fragmentTwo" android:name="com.example.android.navigation.FragmentTwo" android:label="FragmentTwo"> <argument android:name="ShowAppBar" android:defaultValue="true" /> </fragment> </navigation>
Este argumento no se usa en la navegación hacia el destino, sino como una forma de adjuntar información adicional al destino mediante defaultValue
. En este caso, el valor indica si se debe mostrar la barra de la aplicación en este destino.
Ahora podemos agregar un elemento OnDestinationChangedListener
en el objeto Activity
:
Kotlin
navController.addOnDestinationChangedListener { _, _, arguments -> appBar.isVisible = arguments?.getBoolean("ShowAppBar", false) == true }
Java
navController.addOnDestinationChangedListener( new NavController.OnDestinationChangedListener() { @Override public void onDestinationChanged( @NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments ) { boolean showAppBar = false; if (arguments != null) { showAppBar = arguments.getBoolean("ShowAppBar", false); } if(showAppBar) { appBar.setVisibility(View.VISIBLE); } else { appBar.setVisibility(View.GONE); } } } );
El objeto NavController
invoca esta devolución de llamada cada vez que cambia el destino de navegación. Ahora, Activity
puede actualizar el estado o la visibilidad de los componentes de la IU que posee según los argumentos recibidos en la devolución de llamada.
Una de las ventajas de este enfoque es que Activity
solo ve los argumentos en el gráfico de navegación y no conoce las funciones y responsabilidades individuales de Fragment
. De manera similar, los fragmentos individuales no conocen la Activity
que los contiene y los componentes de la IU que posee.
Recursos adicionales
Si deseas obtener más información sobre la navegación, consulta los siguientes recursos adicionales.
Ejemplos
- Ejemplo de navegación básica de los componentes de la arquitectura de Android
- Ejemplo de navegación avanzada de los componentes de la arquitectura de Android
Codelabs
Entradas de blog
Videos
- 10 recomendaciones para pasar a una sola actividad
- Actividad única: Por qué, cuándo y cómo (Android Dev Summit 2018)
- Android Jetpack: Cómo administrar la navegación de la IU con el controlador de Navigation (Google I/O '18)