В этом уроке рассмотрим интеграцию Navigation Component c Overflow Menu, Navigation Drawer и BottomNavigationView.

 

Сначала сделаю небольшое дополнение к прошлым урокам. В них вся работа с NavController велась в Activity, в котором находился контейнер NavHostFragment.

Код получения контроллера в Activity выглядит так:

navController = Navigation.findNavController(this, R.id.nav_host_fragment);

 

Но контроллер может понадобится и в фрагменте, который находится в контейнере. В примерах это были Fragment1, Fragment2 и т.п.

В этих фрагментах, контроллер может быть получен так:

navController = Navigation.findNavController(view);

Где view - это любое View в этом фрагменте.

 

Пример использования в OnClickListener

buttonNext.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       Navigation.findNavController(view).navigate(R.id.fragment2);
   }
});

 

Персонально для OnClickListener, кстати, создан отдельный метод Navigation.createNavigateOnClickListener, позволяющий повесить обработчик на кнопку так:

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.fragment2));

По нажатию на кнопку будет выполнена навигация к fragment2.

 

 

 

NavigationUI

 

NavigationUI - набор методов, позволяющих интегрировать Navigation Component с меню, Navigation Drawer и BottomNavigationView.

Для использования, необходимо добавить в dependencies:

implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha02'

 

 

 

Overflow menu

 

Есть такое меню

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

   <item android:id="@+id/fragment1"
       android:title="Fragment 1"/>

   <item android:id="@+id/fragment2"
       android:title="Fragment 2"/>

   <item android:id="@+id/fragment3"
       android:title="Fragment 3"/>

</menu>

Оно будет отображаться в Activity. 

 

Обратите внимание на ID, которые я использовал в меню: @+id/fragment1, и т.д. Те же ID использованы для destination в графе:

<?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"
   app:startDestination="@id/fragment1">
   <fragment
       android:id="@+id/fragment1"
       android:name="ru.startandroid.navigation.Fragment1"
       android:label="@string/fragment_1_title"
       tools:layout="@layout/fragment1" />
   <fragment
       android:id="@+id/fragment2"
       android:name="ru.startandroid.navigation.Fragment2"
       android:label="@string/fragment_2_title"
       tools:layout="@layout/fragment2" />
   <fragment
       android:id="@+id/fragment3"
       android:name="ru.startandroid.navigation.Fragment3"
       android:label="@string/fragment_3_title"
       tools:layout="@layout/fragment3" />
</navigation>

 

Теперь в обработке нажатий используем метод NavigationUI.onNavDestinationSelected.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   NavigationUI.onNavDestinationSelected(item, navController);
   return super.onOptionsItemSelected(item);
}

Под капотом будет выполнена навигация к destination с ID = item.getItemId().

Соответственно при нажатии на пункт меню с ID = fragment2, будет выполнена навигация к destination с тем же ID, т.е. fragment2.

 

Результат:


Для пунктов меню можно использовать ID не только от destination, но и от action.

 

 

 

Navigation Drawer

 

Есть Drawer, который отображает следующее меню

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

   <group android:checkableBehavior="single">

   <item android:id="@+id/fragment1"
       android:title="Fragment 1"/>

   <item android:id="@+id/fragment2"
       android:title="Fragment 2"/>

   <item android:id="@+id/fragment3"
       android:title="Fragment 3"/>

   </group>

</menu>

Обычно, чтобы обрабатывать нажатия на эти пункты меню, мы вешаем обработчик на NavigationView, который внутри DrawerLayout.

Но вместо этого мы можем сделать так:

NavigationUI.setupWithNavController(navigationView, navController);

Этот метод сам повесит обработчик на NavigationView и по нажатию на пункты меню будет выполнять навигацию к destination (или action) с тем же ID, что и у нажатого пункта меню. Также он сам будет выделять пункт меню (setChecked) и закрывать Drawer.

При этом параметру Pop To будет задан стартовый destination. Т.е. системная кнопка Back всегда будет возвращать нас в Fragment 1.

 

Результат

 

Все ок, но можно сделать еще лучше.

NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

Добавляем интеграцию ActionBar. Теперь при навигации в ActionBar будет помещаться Label у destination. И иконка будет меняться, если находимся не в стартовом destination.

 

Результат

 

 

Нажатие на Home обрабатываем сами

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case android.R.id.home:
           drawerLayout.openDrawer(GravityCompat.START);
           return true;
   }
   return super.onOptionsItemSelected(item);

}

 

 

 

BottomNavigationView

 

Есть BottomNavigationView, отображающий меню:

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

   <item
       android:id="@+id/fragment1"
       android:title="Fragment 1" />

   <item
       android:id="@+id/fragment2"
       android:title="Fragment 2" />

   <item
       android:id="@+id/fragment3"
       android:title="Fragment 3" />

</menu>

 

Чтобы обработать его нажатия, мы обычно вешаем обработчик. За нас это может сделать Navigation.

NavigationUI.setupWithNavController(bottomNavigationView, navController);

Метод setupWithNavController вешает листенер на BottomNavigationView и выполняет навигацию при нажатии на его элементы. При этом выполняет setChecked для нажатого элемента.

 

Результат:

Системная кнопка Back всегда будет возвращать нас на стартовый destination.

 


Можно добавить интеграцию с ActionBar:

NavigationUI.setupActionBarWithNavController(this, navController);

Теперь при навигации в ActionBar будет помещаться Label у destination. И иконка будет меняться, если находимся не в стартовом destination.

 

В этом случае надо самим обработать нажатие на Home.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case android.R.id.home:
           navController.popBackStack();
           return true;
   }

   return super.onOptionsItemSelected(item);
}

 

Результат:

 

 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- тут можно посмотреть, над какими уроками я сейчас работаю, и о чем будут следующие уроки 




Language

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Страница в Facebook