Компонент навигации включает класс NavigationUI
. Этот класс содержит статические методы, которые управляют навигацией с помощью верхней панели приложения, панели навигации и нижней навигации.
Верхняя панель приложений
Верхняя панель приложения обеспечивает единое место в верхней части приложения для отображения информации и действий с текущего экрана.
NavigationUI
содержит методы, которые автоматически обновляют содержимое верхней панели приложения, когда пользователи перемещаются по вашему приложению. Например, NavigationUI
использует метки назначения из вашего графа навигации, чтобы поддерживать актуальность заголовка верхней панели приложения.
<navigation> <fragment ... android:label="Page title"> ... </fragment> </navigation>
При использовании NavigationUI
с реализациями верхней панели приложения, описанными ниже, метка, которую вы прикрепляете к местам назначения, может быть автоматически заполнена из аргументов, предоставленных месту назначения, с использованием формата {argName}
в вашей метке.
NavigationUI
обеспечивает поддержку следующих типов верхних панелей приложений:
Дополнительную информацию о панелях приложений см. в разделе Настройка панели приложений .
Конфигурация AppBar
NavigationUI
использует объект AppBarConfiguration
для управления поведением кнопки навигации в левом верхнем углу области отображения вашего приложения. Поведение кнопки навигации меняется в зависимости от того, находится ли пользователь в пункте назначения верхнего уровня .
Пункт назначения верхнего уровня — это корневой пункт назначения или пункт назначения самого высокого уровня в наборе иерархически связанных пунктов назначения. В пунктах назначения верхнего уровня не отображается кнопка «Вверх» на верхней панели приложения, поскольку пункт назначения более высокого уровня отсутствует. По умолчанию начальный пункт назначения вашего приложения является единственным пунктом назначения верхнего уровня.
Когда пользователь находится в пункте назначения верхнего уровня, кнопка навигации становится значком ящика. если место назначения использует DrawerLayout
. Если пункт назначения не использует DrawerLayout
, кнопка навигации скрыта. Когда пользователь находится в любом другом пункте назначения, кнопка навигации отображается как кнопка «Вверх». . Чтобы настроить кнопку навигации, используя только начальный пункт назначения в качестве пункта назначения верхнего уровня, создайте объект AppBarConfiguration
и передайте соответствующий граф навигации, как показано ниже:
Котлин
val appBarConfiguration = AppBarConfiguration(navController.graph)
Ява
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
В некоторых случаях вам может потребоваться определить несколько пунктов назначения верхнего уровня вместо использования пункта назначения по умолчанию. Использование BottomNavigationView
является распространенным вариантом использования для этого, когда у вас могут быть одноуровневые экраны, которые не связаны иерархически друг с другом и каждый из которых может иметь свой собственный набор связанных пунктов назначения. В подобных случаях вы можете вместо этого передать конструктору набор идентификаторов назначения, как показано ниже:
Котлин
val appBarConfiguration = AppBarConfiguration(setOf(R.id.main, R.id.profile))
Ява
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.main, R.id.profile).build();
Создать панель инструментов
Чтобы создать панель инструментов с помощью NavigationUI
, сначала определите панель в своем основном действии, как показано:
<LinearLayout> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" /> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" ... /> ... </LinearLayout>
Затем вызовите setupWithNavController()
из метода onCreate()
вашего основного действия, как показано в следующем примере:
Котлин
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) }
Ява
@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); }
Чтобы настроить кнопку навигации так, чтобы она отображалась как кнопка «Вверх» для всех мест назначения, передайте пустой набор идентификаторов мест назначения для мест назначения верхнего уровня при создании AppBarConfiguration
. Это может быть полезно, если, например, у вас есть второе действие, которое должно отображать кнопку «Вверх» на Toolbar
во всех местах назначения. Это позволяет пользователю вернуться к родительскому действию, когда в заднем стеке нет других пунктов назначения. Вы можете использовать setFallbackOnNavigateUpListener()
для управления резервным поведением, когда в противном случае navigateUp()
ничего бы не сделал, как показано в следующем примере:
Котлин
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) }
Ява
@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); }
Включить сворачивающуюся панель инструментов
Чтобы включить CollapsingToolbarLayout
в свою панель инструментов, сначала определите панель инструментов и окружающий ее макет в своей деятельности, как показано ниже:
<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>
Затем вызовите setupWithNavController()
из метода onCreate
вашего основного действия, как показано ниже:
Котлин
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) }
Ява
@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); }
Панель действий
Чтобы добавить поддержку навигации в панель действий по умолчанию, вызовите setupActionBarWithNavController()
из метода onCreate()
вашего основного действия, как показано ниже. Обратите внимание, что вам нужно объявить свою AppBarConfiguration
вне onCreate()
, поскольку вы также используете ее при переопределении onSupportNavigateUp()
:
Котлин
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) }
Ява
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); }
Затем переопределите onSupportNavigateUp()
для обработки навигации вверх:
Котлин
override fun onSupportNavigateUp(): Boolean { val navController = findNavController(R.id.nav_host_fragment) return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() }
Ява
@Override public boolean onSupportNavigateUp() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp(); }
Поддержка вариантов панели приложения
Добавление верхней панели приложения в вашу активность работает хорошо, если макет панели приложения одинаков для каждого пункта назначения в вашем приложении. Однако если верхняя панель приложения существенно меняется в разных местах назначения, рассмотрите возможность удаления верхней панели приложения из вашей активности и вместо этого определите ее в каждом фрагменте назначения.
Например, одно из ваших мест назначения может использовать стандартную Toolbar
, а другое — AppBarLayout
для создания более сложной панели приложения с вкладками, как показано на рисунке 2.
Чтобы реализовать этот пример в целевых фрагментах с помощью NavigationUI
, сначала определите панель приложения в каждом макете фрагмента, начиная с целевого фрагмента, который использует стандартную панель инструментов:
<LinearLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
... />
...
</LinearLayout>
Затем определите целевой фрагмент, который использует панель приложения с вкладками:
<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>
Логика настройки навигации одинакова для обоих этих фрагментов, за исключением того, что вам следует вызывать setupWithNavController()
из метода onViewCreated()
каждого фрагмента, а не инициализировать их из действия:
Котлин
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val navController = findNavController() val appBarConfiguration = AppBarConfiguration(navController.graph) view.findViewById<Toolbar>(R.id.toolbar) .setupWithNavController(navController, appBarConfiguration) }
Ява
@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); }
Привязывайте пункты назначения к пунктам меню
NavigationUI
также предоставляет помощники для привязки пунктов назначения к компонентам пользовательского интерфейса, управляемым меню. NavigationUI
содержит вспомогательный метод onNavDestinationSelected()
, который принимает MenuItem
вместе с NavController
, в котором находится связанный пункт назначения. Если id
MenuItem
совпадает с id
пункта назначения, NavController
может затем перейти к этому пункту назначения.
В качестве примера приведенные ниже фрагменты XML определяют пункт меню и пункт назначения с общим id
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>
Если ваше меню было добавлено, например, с помощью onCreateOptionsMenu()
действия, вы можете связать пункты меню с пунктами назначения, переопределив onOptionsItemSelected()
действия для вызова onNavDestinationSelected()
, как показано в следующем примере:
Котлин
override fun onOptionsItemSelected(item: MenuItem): Boolean { val navController = findNavController(R.id.nav_host_fragment) return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item) }
Ява
@Override public boolean onOptionsItemSelected(MenuItem item) { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); return NavigationUI.onNavDestinationSelected(item, navController) || super.onOptionsItemSelected(item); }
Теперь, когда пользователь щелкает пункт меню details_page_fragment
, приложение автоматически переходит к соответствующему пункту назначения с тем же id
.
Добавить панель навигации
Панель навигации — это панель пользовательского интерфейса, на которой отображается главное меню навигации вашего приложения. Ящик появляется, когда пользователь касается значка ящика. в панели приложения или когда пользователь проводит пальцем от левого края экрана.
Значок ящика отображается во всех местах назначения верхнего уровня , которые используют DrawerLayout
.
Чтобы добавить рисовальщик навигации, сначала объявите DrawerLayout
в качестве корневого представления. Внутри DrawerLayout
добавьте макет для основного содержимого пользовательского интерфейса и другое представление, содержащее содержимое панели навигации.
Например, в следующем макете используется DrawerLayout
с двумя дочерними представлениями: NavHostFragment
для хранения основного содержимого и NavigationView
для содержимого панели навигации.
<?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>
Затем подключите DrawerLayout
к графу навигации, передав его в AppBarConfiguration
, как показано в следующем примере:
Котлин
val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
Ява
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()) .setDrawerLayout(drawerLayout) .build();
Затем в классе основного действия вызовите setupWithNavController()
из метода onCreate()
вашего основного действия, как показано ниже:
Котлин
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) }
Ява
@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); }
Начиная с версии Navigation 2.4.0-alpha01 , состояние каждого пункта меню сохраняется и восстанавливается при использовании setupWithNavController
.
Нижняя навигация
NavigationUI
также может обрабатывать нижнюю навигацию. Когда пользователь выбирает пункт меню, NavController
вызывает onNavDestinationSelected()
и автоматически обновляет выбранный элемент в нижней панели навигации.
Чтобы создать нижнюю панель навигации в вашем приложении, сначала определите ее в своем основном действии, как показано ниже:
<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>
Затем в классе основного действия вызовите setupWithNavController()
из метода onCreate()
вашего основного действия, как показано ниже:
Котлин
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) }
Ява
@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); }
Начиная с версии Navigation 2.4.0-alpha01 , состояние каждого пункта меню сохраняется и восстанавливается при использовании setupWithNavController
.
Подробный пример, включающий нижнюю навигацию, см. в примере расширенной навигации по компонентам архитектуры Android на GitHub.
Слушайте события навигации
Взаимодействие с NavController
является основным методом навигации между пунктами назначения. NavController
отвечает за замену содержимого NavHost
новым пунктом назначения. Во многих случаях элементы пользовательского интерфейса, такие как верхняя панель приложения или другие постоянные элементы управления навигацией, такие как BottomNavigationBar
, находятся за пределами NavHost
и должны обновляться при навигации между пунктами назначения.
NavController
предлагает интерфейс OnDestinationChangedListener
, который вызывается, когда текущий пункт назначения NavController
или его аргументы изменяются. Новый прослушиватель можно зарегистрировать с помощью метода addOnDestinationChangedListener()
. Обратите внимание: при вызове addOnDestinationChangedListener()
, если текущий пункт назначения существует, он немедленно отправляется вашему прослушивателю.
NavigationUI
использует OnDestinationChangedListener
чтобы обеспечить навигацию для этих общих компонентов пользовательского интерфейса. Однако обратите внимание, что вы также можете использовать OnDestinationChangedListener
отдельно, чтобы любой пользовательский интерфейс или бизнес-логика знали о событиях навигации.
Например, у вас могут быть общие элементы пользовательского интерфейса, которые вы хотите отображать в одних областях вашего приложения, скрывая их в других. Используя собственный OnDestinationChangedListener
, вы можете выборочно отображать или скрывать эти элементы пользовательского интерфейса в зависимости от целевого пункта назначения, как показано в следующем примере:
Котлин
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 } }
Ява
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); } } });
Слушатели на основе аргументов
В качестве альтернативы вы также можете использовать аргументы со значениями по умолчанию в графе навигации, которые могут использоваться соответствующим контроллером пользовательского интерфейса для обновления своего состояния. Например, вместо того, чтобы основывать логику OnDestinationChangedListener
на идентификаторе назначения, как в предыдущем примере, мы можем создать аргумент в 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>
Этот аргумент не используется при переходе к месту назначения , а скорее как способ прикрепить к месту назначения дополнительную информацию с помощью defaultValue
. В этом случае значение указывает, должна ли отображаться панель приложения в этом месте назначения.
Теперь мы можем добавить OnDestinationChangedListener
в Activity
:
Котлин
navController.addOnDestinationChangedListener { _, _, arguments -> appBar.isVisible = arguments?.getBoolean("ShowAppBar", false) == true }
Ява
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); } } } );
NavController
вызывает этот обратный вызов всякий раз, когда меняется пункт назначения навигации. Activity
теперь может обновлять состояние или видимость принадлежащих ему компонентов пользовательского интерфейса на основе аргументов, полученных в обратном вызове.
Одним из преимуществ этого подхода является то, что Activity
видит только аргументы в графе навигации и не знает ролей и обязанностей отдельных Fragment
. Аналогично, отдельные фрагменты не знают о содержащем Activity
и компонентах пользовательского интерфейса, которыми оно владеет.
Дополнительные ресурсы
Дополнительные сведения о навигации см. в следующих дополнительных ресурсах.
Образцы
- Базовый пример навигации по компонентам архитектуры Android
- Пример расширенной навигации по компонентам архитектуры Android
Кодлабы
Сообщения в блоге
Видео
- 10 лучших практик перехода к одному виду деятельности
- Одиночное мероприятие: почему, когда и как (Android Dev Summit '18)
- Android Jetpack: управление навигацией по пользовательскому интерфейсу с помощью навигационного контроллера (Google I/O '18)