Navigation. NavigationUI
Спочатку зроблю невелике доповнення до минулих уроків. У них уся робота з 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:2.8.5'
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);
}
Результат:
