Navigation Architecture

Среди компонентов 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