Android Studio: уроки на Kotlin. Fragment и Navigation Component

Пользователи ожидают, что приложение будет состоять из нескольких экранов, а перемещение между ними окажется плавным и понятным. Раньше разработчикам приходилось вручную управлять транзакциями фрагментов и писать много служебного кода. Сегодня в Android Studio доступен специальный инструмент — Navigation Component, который позволяет описать карту переходов визуально и связать её с кодом на Kotlin. Этот урок научит вас создавать граф навигации, передавать данные и обрабатывать возврат на предыдущий шаг.


Что даёт Navigation Component

Jetpack Navigation решает три главные задачи: упрощает описание переходов, автоматически управляет back stack (стеком возврата) и обеспечивает типобезопасную передачу аргументов через Safe Args. Вместо того чтобы писать supportFragmentManager.beginTransaction().replace(...), вы просто вызываете метод findNavController().navigate(R.id.action), а всё остальное берёт на себя система. Граф навигации при этом выглядит как наглядная схема, доступная для редактирования прямо в редакторе.


Подключение библиотек

Для начала откройте build.gradle.kts модуля app и добавьте зависимости для Navigation Fragment и Navigation UI. На момент написания статьи актуальна версия 2.8.0, которая работает с Kotlin 2.x.

dependencies {
    implementation("androidx.navigation:navigation-fragment-ktx:2.8.0")
    implementation("androidx.navigation:navigation-ui-ktx:2.8.0")
}

В файле build.gradle.kts уровня проекта также добавьте плагин Safe Args для генерации классов аргументов:

plugins {
    id("androidx.navigation.safeargs.kotlin") version "2.8.0" apply false
}

А в модуле app примените этот плагин:

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

После синхронизации Gradle можно переходить к созданию первого графа.


Создание навигационного графа

Навигационный граф — это XML-файл, в котором перечислены экраны (destinations) и связи между ними. Обычно его помещают в папку res/navigation/. Создайте файл nav_graph.xml, в котором определим два фрагмента: HomeFragment и DetailFragment, а также действие для перехода от первого ко второму.

<?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/nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.app.ui.HomeFragment"
        android:label="Home">
        <action
            android:id="@+id/action_to_detail"
            app:destination="@id/detailFragment" />
    </fragment>

    <fragment
        android:id="@+id/detailFragment"
        android:name="com.example.app.ui.DetailFragment"
        android:label="Detail">
        <argument
            android:name="itemId"
            app:argType="integer"
            android:defaultValue="0" />
    </fragment>
</navigation>

Атрибут startDestination указывает, какой фрагмент показывать при запуске. Через <argument> объявляется параметр, который можно передать из одного фрагмента в другой.


Размещение NavHostFragment в Activity

В разметке activity_main.xml добавьте контейнер FragmentContainerView — именно в нём будет происходить смена экранов. Укажите ему ссылку на граф навигации и задайте android:name="androidx.navigation.fragment.NavHostFragment".

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:navGraph="@navigation/nav_graph"
    app:defaultNavHost="true" />

Параметр defaultNavHost="true" означает, что этот контейнер будет перехватывать системную кнопку «Назад» и обрабатывать её правильно.


Написание фрагментов на Kotlin

Теперь создадим два фрагмента. HomeFragment будет содержать кнопку, при нажатии на которую мы перейдём на DetailFragment с передачей аргумента.

class HomeFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<Button>(R.id.btnNavigate).setOnClickListener {
            val action = HomeFragmentDirections.actionToDetail(itemId = 42)
            findNavController().navigate(action)
        }
    }
}

Класс HomeFragmentDirections сгенерирован плагином Safe Args автоматически на основе графа навигации. Он содержит метод, принимающий нужные аргументы и возвращающий объект действия.


Получение аргументов в DetailFragment

Во втором фрагменте используем тоже сгенерированный класс DetailFragmentArgs, чтобы извлечь переданный идентификатор.

class DetailFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_detail, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val args: DetailFragmentArgs by navArgs()
        val itemId = args.itemId
        view.findViewById<TextView>(R.id.tvDetail).text = "Item ID: $itemId"
    }
}

Делегат navArgs() предоставляет готовый объект с параметрами, который можно сразу использовать.


Навигация назад и up-кнопка

Navigation автоматически управляет кнопкой «Назад» — при её нажатии фрагмент вернётся на предыдущий экран. Для верхней кнопки в Action Bar (стрелочка влево) достаточно связать контроллер с тулбаром через NavigationUI.setupActionBarWithNavController(this, navController). Это делается в onCreate Activity.

val navHostFragment = supportFragmentManager
    .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
NavigationUI.setupActionBarWithNavController(this, navController)

В результате на втором экране автоматически появится стрелка «вверх», ведущая обратно.


Итог

Сегодняшнее занятие в Android Studio показало, как при помощи языка Kotlin и Navigation Component можно выстроить чёткую схему переходов между фрагментами. Вы научились создавать граф навигации, передавать аргументы с использованием Safe Args и настраивать корректную обработку системной кнопки «Назад». Полученные навыки позволят вам делать многоэкранные приложения с предсказуемой навигацией, не тратя время на ручное управление транзакциями. Попробуйте добавить анимации переходов и условную навигацию — это естественное продолжение сегодняшнего урока.