В этом уроке рассмотрим интеграцию Navigation Component c Overflow Menu, Navigation Drawer и BottomNavigationView.
Полный список уроков курса:
- Урок 1. Lifecycle
- Урок 2. LiveData
- Урок 3. LiveData. Дополнительные возможности
- Урок 4. ViewModel
- Урок 5. Room. Основы
- Урок 6. Room. Entity
- Урок 7. Room. Insert, Update, Delete, Transaction
- Урок 8. Room. Query
- Урок 9. Room. RxJava
- Урок 10. Room. Запрос из нескольких таблиц. Relation
- Урок 11. Room. Type converter
- Урок 12. Room. Миграция версий базы данных
- Урок 13. Room. Тестирование
- Урок 14. Paging Library. Основы
- Урок 15. Paging Library. PagedList и DataSource. Placeholders.
- Урок 16. Paging Library. LivePagedListBuilder. BoundaryCallback.
- Урок 17. Paging Library. Виды DataSource
- Урок 18. Android Data Binding. Основы
- Урок 19. Android Data Binding. Код в layout. Доступ к View
- Урок 20. Android Data Binding. Обработка событий
- Урок 21. Android Data Binding. Observable поля. Двусторонний биндинг.
- Урок 22. Android Data Binding. Adapter. Conversion.
- Урок 23. Android Data Binding. Использование с include, ViewStub и RecyclerView.
- Урок 24. Navigation Architecture Component. Введение
- Урок 25. Navigation. Передача данных. Type-safe аргументы.
- Урок 26. Navigation. Параметры навигации
- Урок 27. Navigation. NavigationUI.
- Урок 28. Navigation. Вложенный граф. Global Action. Deep Link.
- Урок 29. WorkManager. Введение
- Урок 30. WorkManager. Критерии запуска задачи.
- Урок 31. WorkManager. Последовательность выполнения задач.
- Урок 32. WorkManager. Передача и получение данных
- Урок 33. Практика. О чем это будет.
- Урок 34. Практика. TodoApp. Список задач.
- Урок 35. Практика. TodoApp. Просмотр задачи
Сначала сделаю небольшое дополнение к прошлым урокам. В них вся работа с 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, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня