Navigation

El componente Navigation incluye una clase NavigationUI. Esta clase contiene métodos estáticos que administran la navegación con el NavigationView, el BottomNavigationView y el OptionsMenu.

Además de usar estos elementos de navegación, en esta práctica usaremos también la navegación mediante Tabs (o pestañas), usando los elementos TabLayout y ViewPager2, aunque estos no están incluidos en el componente Navigation.

Desarrollaremos una app que permitirá navegar entre destinos usando todos estos elementos.

https://github.com/gerardfp/navigation

Crea el proyecto

En el asistente de "Nueva Activity", Android Studio proporciona plantillas para crear Activities que incluyen estos componentes de navegación. Sin embargo, esta vez lo haremos "desde 0", para comprender todos los elementos que intervienen.

  1. Selecciona Empty Activity como plantilla para la MainActivity

  2. Añade las dependencias para incluir el componente Navigation, ViewPager2 y Material:

    build.gradle (Module: app) dependencies { implementation 'androidx.navigation:navigation-fragment:2.3.0' implementation 'androidx.navigation:navigation-ui:2.3.0' implementation 'androidx.viewpager2:viewpager2:1.0.0' implementation 'com.google.android.material:material:1.3.0-alpha03' }
  3. Habilita el ViewBinding:

    build.gradle (Module: app) android { buildFeatures { viewBinding true } }
  4. Por último, deshabilitaremos la ActionBar (la barra superior) que se incluye de forma automática en todas las Activities, ya que en este proyecto utilizaremos la versión más reciente Toolbar, que se integra mejor con los elementos de navegación:

    Añade los siguientes elementos al fichero styles.xml:

    res/values/styles.xml <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ... <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style> </resources>

    Para más info sobre la actualización de la ActionBar por la Toolbar, consulta: Cómo configurar la barra de la app

Activities

Cada uno de elementos de navegación (NavigationView, BottomNavigationView, OptionsMenu y TabLayout + ViewPager2) lo implementaremos en una Activity diferente y usaremos la MainActivity para lanzar las diferentes Activities.

Empezaremos creando estas Activities, con los nombres: DrawerActivity, BottomActivity, OptionsActivity y TabbedActivity.

Para crear una Activity selcciona NewActivityEmpty Activity

Observa que cuando creas una Activity con el asistente, e introduces un nombre como AbcActivity, automáticamente se crea un archivo AbcActivity.java para la clase, y otro activity_abc.xml para el layout.

También se crea una entrada para la activity en el elemento <application> del fichero AndroidManifest.

Toolbar

Ahora el siguinte paso será añadir una Toolbar en cada una de estas activities.

La Toolbar se utilitza conjuntamente con el CoordinatorLayout y el AppBarLayout que permiten utilizar transiciones y animaciones (por ejemplo una Collapsing Toolbar). Aunque en esta práctica no las haremos, mejor si la usamos desde el principio.

Sustituye el layout de cada una de las 5 activities por este layout:

res/layout/activity_$$$.xml <?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </com.google.android.material.appbar.AppBarLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <!-- Aqui va el contenido de la activity --> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

MainActivity

El siguiente paso es añadir 4 botones en la MainActivity para que cada uno nos lleve a una de las otras Activities.

En el <ConstraintLayout> de la activity_main.xml añade los 4 botones:

res/layout/activity_main.xml <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/action_goto_drawerActivity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Drawer" app:layout_constraintBottom_toTopOf="@id/action_goto_bottomActivity" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/action_goto_bottomActivity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bottom" app:layout_constraintBottom_toTopOf="@id/action_goto_optionsActivity" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/action_goto_drawerActivity" /> <Button android:id="@+id/action_goto_optionsActivity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Options" app:layout_constraintBottom_toTopOf="@id/action_goto_tabbedActivity" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/action_goto_bottomActivity" /> <Button android:id="@+id/action_goto_tabbedActivity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tabs" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/action_goto_optionsActivity" /> </androidx.constraintlayout.widget.ConstraintLayout>

Ahora implementamos el comportamiento del evento clic, añadiendo un OnClickListener en cada botón, de manera que cuando se haga clic en un botón se iniciará la activity correspondiente.

Para iniciar una Activity hay que llamar al método startActivity() y pasarle un objeto de clase Intent, indicando en los parámetros del constructor el objeto de la Activity en la que estamos y la clase de la Activity que queremos iniciar.

Reemplaza el código de la clase MainActivity por este (¡excepto la primera linea del package!). Usa Alt + Intro para importar la clase de viewbinding:

MainActivity.java import android.content.Intent; import android.os.Bundle; import android.view.View; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((binding = ActivityMainBinding.inflate(getLayoutInflater())).getRoot()); setSupportActionBar(binding.toolbar); binding.actionGotoDrawerActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, DrawerActivity.class)); } }); binding.actionGotoBottomActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, BottomActivity.class)); } }); binding.actionGotoOptionsActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, OptionsActivity.class)); } }); binding.actionGotoTabbedActivity.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, TabbedActivity.class)); } }); } }

La llamada a setSupportActionBar() 15 hace que la Activity utilice la Toolbar que hemos puesto en el layout, en lugar de la ActionBar que venia por defecto, y que hemos ocultado.

Observa que el primer parámetro del Intent siempre es MainActivity.this, es decir, el objeto correspondiente a la MainActivity (la activity en la que estamos). En cambio el segundo parámetro es la clase .class de la activity que queremos iniciar en cada botón.

Si ejecutas ahora la app, comprobarás que cada botón nos lleva a una Activity, que todavía está en blanco. En los siguientes pasos, implementaremos la navegación usando un menú diferente para cada una de estas activities.

NavigationView

El NavigationView (más conocido como "Drawer menu") es el menú de navegación que suele aparecer por la izquierda de la app cuando se pulsa el icono Hamburger .

En la DrawerActivity de esta app usaremos un NavigationView para navegar a 3 destinos (fragments).

Empezamos creando el Grafo de Navegación, al que llamaremos drawer_graph.xml. En él añadimos los 3 destinos. Para tener presente que estos destinos serán accesibles desde el Drawer, les llamaremos: Drawer1Fragment, Drawer2Fragment, Drawer3Fragment.

Crea el grafo drawer_graph.xml y añade los 3 destinos:

En este grafo de navegación no hemos creado ninguna acción de navegación, ya que los 3 destinos son independientes ente sí, es decir, no se navega de ninguno a otro, sino que todos son accesibles desde el menú drawer.

El siguiente paso lógico debería ser añadir el NavHostFragment en el layout activity_drawer.xml, pero no lo hacemos ahora ya que en el siguiente paso hay que volver a modificar el layout.

Añadir el NavigationView

Android proporciona el DrawerLayout para ser utilizado con el NavigationView. Su única función es permitir que el NavigationView pueda ponerse "por encima" de los otros elementos.

Añadiremos el NavigationView a la DrawerActivity. Reemplaza el contenido del fichero res/layout/activity_drawer.xml por este:

res/layout/activity_drawer.xml <?xml version="1.0" encoding="utf-8"?> <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"> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </com.google.android.material.appbar.AppBarLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <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/drawer_graph" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> <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" app:headerLayout="@layout/drawer_header" app:menu="@menu/drawer_menu"/> </androidx.drawerlayout.widget.DrawerLayout>

Observa que dentro del DrawerLayout hemos puesto el CoordinatorLayout que ya teníamos y el NavigationView.

Además hemos añadido el NavHostFragment que nos habíamos saltado antes. En él es donde se mostrarán los 3 destinos que hemos creado en el grafo drawer_graph.xml.

Cabecera y menú

Observa que hay dos atributos XML en el elemento NavigationView que aparecen en rojo. Estos son el app:headerLayout y el app:menu. Estos dos atributos definen los ficheros en que se encuentran el layout de la cabecera y los ítems del menú:

Crearemos estos dos ficheros, y definiremos la cabecera y el menú:

NavigationUI

La clase NavigationUI, es la encargada de administrar la navegación. Cuando se haga clic en un ítem del menú, se encargará de:

  1. resaltar el ítem seleccionado en el menú

  2. cambiar el título en la Toolbar

  3. mostrar el destino (fragment) seleccionado en el NavHostFragment

  4. gestionar el icono hamburguer (mostrando el o la flecha atrás )

Para activar y configurar NavigationUI, hay que llamar a una serie de métodos estáticos.

Llamaremos a estos métodos desde la DrawerActivity, ya que en ella hemos puesto el NavigationView, la Toolbar y el NavHostFragment.

El código de la DrawerActivity debe quedar así:

DrawerActivity.java import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; public class DrawerActivity extends AppCompatActivity { ActivityDrawerBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((binding = ActivityDrawerBinding.inflate(getLayoutInflater())).getRoot()); setSupportActionBar(binding.toolbar); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( // Top-level destinations: R.id.drawer1Fragment, R.id.drawer2Fragment ) .setOpenableLayout(binding.drawerLayout) .build(); NavController navController = ((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment)).getNavController(); NavigationUI.setupWithNavController(binding.navView, navController); NavigationUI.setupWithNavController(binding.toolbar, navController, appBarConfiguration); } }

Hay 2 cosas que obsevar en este código:

Ahora puedes ejecutar la app, abrir la DrawerActivity, y navegar usando el NaviagtionView por los 3 destinos: Drawer1Fragment, Drawer2Fragment y Drawer3Fragment. Observa cómo en el tercer fragment, se muestra la flecha de "atrás" , ya que no es un destino Top-level.

BottomNavigationView

El BottomNavigationView es la barra de menú que se muestra en la parte inferior de la pantalla.

Añadiremos este menú en la BottomActiviy para navegar a 3 destinos.

Creamos el grafo de navegación bottom_graph.xml, y añadimos tres destinos a los que llamaremos Bottom1Fragment, Bottom2Fragment y Bottom3Fragment:

Añadimos el NavHostFragment en el layout de la BottomActivity. En él se mostrarán los destinos según se vaya navegando.

Añade el NavHostFragment dentro del ConstraintLayout:

res/layout/activity_bottom.xml <androidx.constraintlayout.widget.ConstraintLayout ... > <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/bottom_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>

Observa que hemos enlazado el NavHostFragment con el grafo bottom_graph.

Añadir el BottomNavigationView al layout

Añade el elemento <BottomNavigationView> al layout de la BottomActivity. Colócalo dentro del ConstraintLayout, justo debajo del NavHostFragment:

res/layout/activity_bottom.xml <androidx.constraintlayout.widget.ConstraintLayout ...> <androidx.fragment.app.FragmentContainerView .../> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav_view" android:layout_width="0dp" android:layout_height="wrap_content" android:background="?android:attr/windowBackground" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:menu="@menu/bottom_menu" /> </androidx.constraintlayout.widget.ConstraintLayout>

Menu

Crea el archivo de menú para el BottomNavigationView (res/menu/bottom_menu.xml):

Añade los 3 ítems al menú (el id del item debe coincidir con el id del destino):

res/menu/bottom_menu.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/bottom1Fragment" android:icon="@android:drawable/ic_menu_directions" android:title="Bottom 1" /> <item android:id="@+id/bottom2Fragment" android:icon="@android:drawable/ic_menu_directions" android:title="Bottom 2" /> <item android:id="@+id/bottom3Fragment" android:icon="@android:drawable/ic_menu_directions" android:title="Bottom 3" /> </menu>

NavigationUI

Ahora configuraremos NavigationUI en la BottomActivity, para que administre la navegación del BottomNavigationView, y sincronice los títulos en la Toolbar.

El código de la BottomActivity debe quedar así:

BottomActivity.java import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.NavigationUI; public class BottomActivity extends AppCompatActivity { ActivityBottomBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((binding = ActivityBottomBinding.inflate(getLayoutInflater())).getRoot()); setSupportActionBar(binding.toolbar); NavController navController = ((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment)).getNavController(); NavigationUI.setupWithNavController(binding.bottomNavView, navController); NavigationUI.setupWithNavController(binding.toolbar, navController); } }

Ejecuta la app, y comprueba que en la BottomActivity se puede navegar por los 3 destino usando el BottomNavigationView.

OptionsMenu

El OptionsMenu es el menu desplegable que aparece en la parte superior derecha cuando se hace clic en el icono de los 3 puntos

Lo usaremos para navegar a 3 destinos

Crea el grafo de navegación options_graph.xml y añade 3 destinos: Options1Fragment, Options2Fragment y Options3Fragment.

Añade el NavHostFragment en el layout activity_options.xml. Colócalo dentro del ConstraintLayout:

res/layout/activity_options.xml <androidx.constraintlayout.widget.ConstraintLayout ...> <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/options_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>

Observa que hemos enlazado el NavHostFragment con el grafo options_graph.

Menu

Crea el archivo de menú para el OptionsMenu (res/menu/options_menu.xml). Añade los ítems:

res/menu/options_menu.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/options1Fragment" android:icon="@android:drawable/ic_menu_directions" android:title="Options 1" app:showAsAction="always"/> <item android:id="@+id/options2Fragment" android:icon="@android:drawable/ic_menu_directions" android:title="Options 2" /> <item android:id="@+id/options3Fragment" android:icon="@android:drawable/ic_menu_directions" android:title="Options 3" /> </menu>

En el item options1Fragment hemos añadido el atributo app:showAsAction="always", esto hará que esta opción del menú no se muestre en el desplegable, sino que se muestre como un icono en la barra.

NavigationUI

Por último, configuraremos NavigationUI para que gestione la navegación del Options Menu.

OptionsActivity.java import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; public class OptionsActivity extends AppCompatActivity { ActivityOptionsBinding binding; private NavController navController; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((binding = ActivityOptionsBinding.inflate(getLayoutInflater())).getRoot()); setSupportActionBar(binding.toolbar); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( R.id.options1Fragment, R.id.options2Fragment ) .build(); navController = ((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment)).getNavController(); NavigationUI.setupWithNavController(binding.toolbar, navController, appBarConfiguration); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.options_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { return NavigationUI.onNavDestinationSelected(item, navController) || super.onOptionsItemSelected(item); } }

En esta activity se han anulado (@Override) dos métodos:

Ejecuta la app y comprueba la navegación con el OptionsMenu. Observa que el primer ítem aparece como un icono en la barra. Y también que cuando se navega al tercer fragment, aparece el boton de "atrás" , ya que no está puesto como destino Top-level.

TabLayout y ViewPager2

El elemento ViewPager2 permite crear vistas deslizantes a las que se puede navegar deslizando el dedo. El elemento TabLayout añade unas pestañas en la parte superior.

Estos elementos no forman parte del componente Navigation, y no son gestionados por NavigationUI.

En la activity TabbedActivity, añadiremos un fragment que incluirá un ViewPager2 y un TabLayout, que permitirán deslizarse entre 3 fragments (A, B y C).

Empezaremos creando el grafo de navegación de la TabbedActivity, al que llamaremos tabbed_graph.xml. En este grafo añadimos únicamente el fragment Tabbed1Fragment.

Añadimos el NavHostFragment al layout activity_tabbed.xml:

res/layout/activity_tabbed.xml <androidx.constraintlayout.widget.ConstraintLayout ...> <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/tabbed_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>

En este NavHostFragment únicamente se mostrará el Tabbed1Fragment.

Crear las páginas deslizantes

Crearemos 3 fragments (A, B, C), que serán los que se deslizen por el Tabbed1Fragment.

Estos 3 fragments no se crean en el grafo de navegación, ya que no se navegará entre ellos usando Navigation, sino usando el ViewPager2.

Llamaremos a estos fragments: Tabbed1AFragment, Tabbed1BFragment y Tabbed1CFragment.

Para crear un Fragment: clic-derecho sobre app y New Fragment Fragment (Blank).

Crea los 3 fragments: Tabbed1AFragment, Tabbed1BFragment y Tabbed1CFragment.

Añadir el ViewPager2

En el layout del Tabbed1Fragment añadimos el ViewPager2. En él se mostrarán los 3 fragments (A,B,C) que hemos creado.

res/layout/fragment_tabbed1.xml <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>

Para vincular los 3 fragments (A,B,C) con el ViewPager2, hay que llamar al método setAdapter() y pasárle un objeto FragmentStateAdapter con la configuración.

Realizaremos esta llamada en el método onViewCreated() del Tabbed1Fragment.

La clase Tabbed1Fragment debe quedar así:

Tabbed1Fragment.java import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; public class Tabbed1Fragment extends Fragment { private FragmentTabbed1Binding binding; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return (binding = FragmentTabbed1Binding.inflate(inflater, container, false)).getRoot(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); binding.viewPager.setAdapter(new FragmentStateAdapter(this) { @NonNull @Override public Fragment createFragment(int position) { switch (position) { case 0: default: return new Tabbed1AFragment(); case 1: return new Tabbed1BFragment(); case 2: return new Tabbed1CFragment(); } } @Override public int getItemCount() { return 3; } }); } }

El objeto de clase FragmentStateAdapter configura el viewPager anulando dos métodos:

Si ejecutas la app, verás que se pueden deslizar los 3 fragments (A,B,C).

Añadir el TabLayout

En el layout fragment_tabbed1.xml añadimos el <TabLayout> justo arriba del ViewPager2. Reemplaza el contenido por el siguiente:

res/layout/fragment_tabbed1.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content"/> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>

Hemos cambiado el FrameLayout por un LinearLayout con orientacion vertical, para que el TabLayout y el ViewPager2 se muestren un encima el otro.

Para vincular el TabLayout con el ViewPager2, de forma que la página cambie cuando se pulsa en una pestaña, y viceversa, hay que crear un objeto TabLayoutMediator al que se le pasan el TabLayout y ViewPager2. También se le pasa un TabConfigurationStrategy indicando el titulo que se debe mostrar en cada Tab. Por último, se llama al método attach() de este objeto.

Añade el código resaltado en Tabbed1Fragment:

Tabbed1Fragment.java public class Tabbed1Fragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { ... binding.viewPager.setAdapter(new FragmentStateAdapter(this) { ... }); new TabLayoutMediator(binding.tabLayout, binding.viewPager, new TabLayoutMediator.TabConfigurationStrategy() { @Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) { switch (position) { case 0: default: tab.setText("TAB A"); break; case 1: tab.setText("TAB B"); break; case 2: tab.setText("TAB C"); break; } } }).attach(); } }

Ahora ya se muestran los Tabs, con el título y sincronizados con las páginas.

Práctica

Desarrolla una app con una sola Activity, que implemente la navegación usando los elementos NavigationView, BottomNavigationView, OptionsMenu y ViewPager2 + TabLayout.