Среди компонентов Jetpack, самым интересным является Navigation Architecture Component. Навигация управляет перемещением между destinations. Destinations обычно представлены фрагментами, однако поддерживают activities и другие пользовательские destinations. С помощью Navigation Architecture Component можно с легкостью реализовать сложную навигацию в android. Он предоставляет набор компонентов навигации, таких как Fragment transactions, Up and Back navigation, которые обрабатывают большую часть информации. Помимо этого, предоставляются шаблоны Navigation UI, такие как navigation drawers, bottom navigation и многие другие, содержащие минимальное количество кода. Узнать подробнее о преимуществах и ключевых особенностях Navigation Architecture Component можно, посмотрев официальную документацию.
Прежде чем начать…
Ниже изображена демо-версия приложения, которое мы будем создавать.
Приложения содержат три фрагмента: HomeFragment, ProfileFragment и SettingsFragment в Bottom Navigation View. В HomeFragment есть кнопка, которая возвращает к FirstPageFragment. FirstPageFragment ведет к LastpageFragment.
Добавление зависимостей
На уровне приложения build.gradle
добавьте следующие зависимости.
implementation "android.arch.navigation:navigation-fragment-ktx:$rootProject.navigationVersion" //get the lastest version
implementation "android.arch.navigation:navigation-ui-ktx:$rootProject.navigationVersion"
Также добавьте следующую зависимость.
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationSageArgs" //get the latest version
Следующий плагин предназначен для передачи аргументов между destinations (в нашем случае фрагментов).
apply plugin: 'androidx.navigation.safeargs'
Установка MainActivity
MainActivity содержит фрагмент NavHost и Bottom Navigation View в XML layout.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:tools = "http://schemas.android.com/tools"
xmlns:app = "http://schemas.android.com/apk/res-auto"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
app:layout_behavior = "@string/appbar_scrolling_view_behavior"
tools:showIn = "@layout/activity_main"
tools:context = ".MainActivity">
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:navGraph="@navigation/android_navigation"
app:layout_constraintBottom_toTopOf="@+id/bottom_nav_view"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/nav_drawer_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
Фрагмент Navhost: Activity размещает навигацию для приложения в NavHost. NavHost — это пустой контейнер, который перемещает destinations фрагментов по навигационному графу (navigation graph).
Во фрагменте NavHost назначаем свойство navGraph
, значение которого представлено XML-ресурсом. Этот XML-ресурс называется навигационным графом, который представляет собой отдельные узлы destination в приложении вместе с соединяющими узлы действиями. Другими словами, destinations — это фрагменты, которые получаются в результате действий. Для создания навигационного графа создайте каталог navigation
под каталогом res
. Затем создайте навигационный XML-ресурс в этом каталоге.
Навигационный граф для нашего приложения выглядит следующим образом. Строки представляют собой действия, приводящие к определенным destinations. Дизайн android_navigation.xml
выглядит так.
Здесь представлена текстовая часть android_navigation.xml
.
<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"
android:id = "@+id/android_navigation"
app:startDestination = "@+id/home_fragment">
<fragment
android:id = "@+id/home_fragment"
android:name = "com.example.androidnavigation.fragments.HomeFragment"
tools:layout = "@layout/home_fragment">
<action
android:id = "@+id/action_goto1"
app:destination = "@id/first_page_fragment">
</action>
</fragment>
<fragment
android:id = "@+id/profile_fragment"
android:name = "com.example.androidnavigation.fragments.ProfileFragment"
tools:layout = "@layout/home_fragment"
>
</fragment>
<fragment
android:id = "@+id/settings_fragment"
android:name = "com.example.androidnavigation.fragments.SettingsFragment"
tools:layout = "@layout/home_fragment"
/>
<fragment
android:id = "@+id/first_page_fragment"
android:name = "com.example.androidnavigation.fragments.FirstPageFragment"
tools:layout = "@layout/first_page_fragment"
>
<action
android:id = "@+id/action_go_home"
app:destination = "@+id/last_page_fragment" />
</fragment>
<fragment
android:id = "@+id/last_page_fragment"
android:name = "com.example.androidnavigation.fragments.LastPageFragment"
tools:layout = "@layout/lastpage_fragment">
<action
android:id="@+id/action_go_home"
app:popUpTo="@+id/home_fragment"
/>
</fragment>
</navigation>
Но…кто все это контролирует?
NavController. Это объект, который отслеживает текущую позицию в навигационном графе. Он координирует перемещение содержимого destination в NavHostFragment по навигационному графу. В MainActivity создаем объект NavController.
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
Перейти к destinations можно с помощью NavController.
findNavController().navigate(R.id.first_page_fragment)
Установка Bottom Navigation
С помощью Navigation Architecture Component можно с легкостью создать навигацию с menus, drawers и bottom navigation. Для установки bottom navigation view понадобятся лишь две строки.
private fun setUpBottomNav(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
bottomNav?.setupWithNavController(navController)
}
Таким образом, мы уже создали три фрагмента Bottom Navigation View.
Action!
Помимо NavController для навигации также можно использовать действия. Строки, показанные на навигационном графе — это действия.
<fragment
android:id = "@+id/home_fragment"
android:name = "com.example.androidnavigation.fragments.HomeFragment"
tools:layout = "@layout/home_fragment">
<action
android:id = "@+id/action_goto1"
app:destination = "@id/first_page_fragment">
</action>
</fragment>
Id действия action_goto1
соединяет home_fragment
с destination first_page_fragment
. При использовании app:popUpTo
вместо app: destination
, фрагменты удаляются из обратного стека до момента достижения first_page_fragment
.
view.findViewById<Button>(R.id.btn_click_me)?.setOnClickListener ( Navigation.createNavigateOnClickListener(R.id.action_goto1)
)
Приступим к работе над аргументами!
ПлагинGradle генерирует простой объект и строитель классов для типобезопасного доступа к аргументам, установленным для destinations и действий. Тег <argument>
в first_page_fragment
генерирует класс FirstPageFragmentArgs. Передаем целое число testNumber
из HomeFragment к FirstPageFragment.
Запомните, тег аргумента определяется во фрагменте, который получает аргументы.
<fragment
android:id = "@+id/first_page_fragment"
android:name = "com.example.androidnavigation.fragments.FirstPageFragment"
tools:layout = "@layout/first_page_fragment"
>
<argument
android:name = "testNumber"
app:argType = "integer"
android:defaultValue = "1"
/>
<action
android:id = "@+id/action_go_home"
app:destination = "@+id/last_page_fragment" />
</fragment>
Класс FirstPageFragmentArgs генерирует геттеры и сеттеры для аргумента testNumber
. Устанавливаем значение в HomeFragment с помощью классов Directions.
var action: HomeFragmentDirections.ActionGoto1 =
HomeFragmentDirections.actionGoto1()
action.setTestNumber(1234)
Navigation.findNavController(view).navigate(action);
Классы Directions генерируются для каждого отдельного destination с действиями. Класс Directions включает методы для каждого действия в destination.
С помощью следующих строк получаем значение testNumber
в FirstPageFragment.
val safeArgs = FirstPageFragmentArgs.fromBundle(arguments)
val flowStepNumber = safeArgs.testNumber
Вот и все! Мы успешно использовали Navigation Architecture Component для создания приложения. Запустите приложение и проверьте результат. Полный код можно посмотреть на github. Счастливого программирования!
Перевод статьи Reema Prajapati: Getting started with Navigation Architecture Component