Как создать граф навигации

Компонент Navigation использует граф навигации для управления навигацией в приложении. Граф навигации – это структура данных, которая содержит все пункты назначения в приложении и связи между ними.

Типы целевых страниц

Существует три основных типа пунктов назначения: размещенные, диалоговые и действия. В таблице ниже описаны эти три типа целевых страниц и их назначение.

Тип

Описание

Примеры использования

Размещенные

Заполняет весь хост навигации. То есть размер размещенного пункта назначения совпадает с размером хоста навигации, а предыдущие пункты назначения не видны.

Главный экран и экран с подробной информацией.

Диалоговое окно

Показывает компоненты интерфейса оверлея. Этот интерфейс не привязан к местоположению или размеру хоста навигации. Предыдущие пункты назначения видны под пунктом назначения.

Оповещения, выбор, формы.

Действия

Представляет уникальные экраны или функции в приложении.

Служит точкой выхода из графа навигации, которая запускает новое действие Android, управляемое отдельно от компонента Navigation.

В современной разработке для Android приложение состоит из одного действия. Поэтому целевые местоположения действий лучше всего использовать при взаимодействии со сторонними действиями или в рамках процесса переноса.

В этом документе приведены примеры размещенных целевых страниц, которые являются наиболее распространенными и основными. Информацию о других целевых страницах можно найти в следующих руководствах:

Компоненты Framework

Хотя во всех случаях применяется один и тот же общий рабочий процесс, то, как именно вы создаете хост навигации и граф, зависит от используемой вами платформы пользовательского интерфейса.

  • Compose. Используйте composable-функцию NavHost. Добавьте в него NavGraph с помощью Kotlin DSL. График можно создать двумя способами:
    • В составе NavHost. Граф навигации создается непосредственно при добавлении NavHost.
    • Программно. Используйте метод NavController.createGraph(), чтобы создать NavGraph и передать его непосредственно в NavHost.
  • Фрагменты. При использовании фрагментов с фреймворком Views UI используйте NavHostFragment в качестве хоста. Граф навигации можно создать несколькими способами:
    • Программно. Используйте Kotlin DSL, чтобы создать NavGraph и применить его непосредственно к NavHostFragment.
      • Функция createGraph(), используемая с Kotlin DSL, одинакова и для фрагментов, и для Compose.
    • XML. Напишите хост навигации и граф непосредственно в XML.
    • Редактор Android Studio. Используйте редактор графического интерфейса пользователя в Android Studio, чтобы создать и настроить граф в виде XML-файла ресурсов.

Написать

В Compose используйте сериализуемый объект или класс, чтобы определить маршрут. Маршрут описывает, как добраться до пункта назначения, и содержит всю информацию, необходимую для пункта назначения.

Используйте аннотацию @Serializable, чтобы автоматически создавать необходимые методы сериализации и десериализации для типов маршрутов. Эта аннотация предоставляется плагином Kotlin Serialization. Следуйте этим инструкциям, чтобы добавить плагин.

Определив маршруты, используйте composable-функцию NavHost, чтобы создать граф навигации. Пример:

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // Add more destinations similarly.
}
  1. Каждый из двух маршрутов, Profile и FriendsList, представлен сериализуемым объектом.
  2. Вызов composable-функции NavHost передает NavController и маршрут для начального пункта назначения.
  3. Лямбда-выражение, переданное в NavHost, в конечном итоге вызывает NavController.createGraph() и возвращает NavGraph.
  4. Каждый маршрут передается в качестве аргумента типа в NavGraphBuilder.composable<T>(), который добавляет пункт назначения в результирующий NavGraph.
  5. Лямбда-функция, переданная в composable, – это то, что NavHost показывает для этого пункта назначения.

Как работает лямбда-функция

Чтобы лучше понять лямбда-выражение, создающее NavGraph, представьте, что для построения того же графа, что и в предыдущем фрагменте, вы можете создать NavGraph отдельно с помощью NavController.createGraph() и передать его непосредственно в NavHost:

val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)

Передача аргументов

Если вам нужно передать данные в пункт назначения, определите маршрут с помощью класса, у которого есть параметры. Например, маршрут Profile – это класс данных с параметром name.

@Serializable
data class Profile(val name: String)

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

Для необязательных аргументов создайте поля, допускающие значение NULL, со значением по умолчанию.

@Serializable
data class Profile(val nickname: String? = null)

Получение экземпляра маршрута

Экземпляр маршрута можно получить с помощью NavBackStackEntry.toRoute() или SavedStateHandle.toRoute(). При создании целевой страницы с помощью composable() параметр NavBackStackEntry доступен.

@Serializable
data class Profile(val name: String)

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(name = profile.name) }
}

Обратите внимание на следующее:

  • Маршрут Profile указывает начальный пункт назначения в графе навигации, а "John Smith" – аргумент для name.
  • Целевая страница – это блок composable<Profile>{}.
  • Функция ProfileScreen принимает значение profile.name для собственного аргумента name.
  • Таким образом, значение "John Smith" передается в ProfileScreen.

Минимальный пример

Ниже приведен полный пример совместного использования NavController и NavHost.

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(
    profile: Profile
    onNavigateToFriendsList: () -> Unit,
  ) {
  Text("Profile for ${profile.name}")
  Button(onClick = { onNavigateToFriendsList() }) {
    Text("Go to Friends List")
  }
}

// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
  Text("Friends List")
  Button(onClick = { onNavigateToProfile() }) {
    Text("Go to Profile")
  }
}

// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
  val navController = rememberNavController()
  NavHost(navController, startDestination = Profile(name = "John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(
            profile = profile,
            onNavigateToFriendsList = {
                navController.navigate(route = FriendsList)
            }
        )
    }
    composable<FriendsList> {
      FriendsListScreen(
        onNavigateToProfile = {
          navController.navigate(
            route = Profile(name = "Aisha Devi")
          )
        }
      )
    }
  }
}

Как показано во фрагменте, вместо передачи NavController в composable-функции, передайте событие в NavHost. То есть у composable-функций должен быть параметр типа () -> Unit, для которого NavHost передает лямбда-выражение, вызывающее NavController.navigate().

Фрагменты

Как описано в предыдущих разделах, при использовании фрагментов у вас есть возможность создать граф навигации программно с помощью Kotlin DSL, XML или редактора Android Studio.

Ниже подробно описаны эти подходы.

Программно

Kotlin DSL предоставляет программный способ создания графа навигации с фрагментами. Во многих отношениях это более аккуратный и современный способ, чем использование XML-файла ресурсов.

Рассмотрим следующий пример, в котором реализован граф навигации с двумя экранами.

Сначала создайте объект NavHostFragment, который не должен содержать элемент app:navGraph:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <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" />
</FrameLayout>

Затем передайте id объекта NavHostFragment в NavController.findNavController. Это связывает NavController с NavHostFragment.

Затем вызов NavController.createGraph() связывает граф с NavController и, следовательно, с NavHostFragment:

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// Retrieve the NavController.
val navController = findNavController(R.id.nav_host_fragment)

// Add the graph to the NavController with `createGraph()`.
navController.graph = navController.createGraph(
    startDestination = Profile(name = "John Smith")
) {
    // Associate each destination with one of the route constants.
    fragment<ProfileFragment, Profile> {
        label = "Profile"
    }

    fragment<FriendsListFragment, FriendsList>() {
        label = "Friends List"
    }

    // Add other fragment destinations similarly.
}

Использование DSL таким образом очень похоже на рабочий процесс, описанный в предыдущем разделе Compose. Например, и там, и здесь функция NavController.createGraph() генерирует NavGraph. Аналогично, в то время как NavGraphBuilder.composable() добавляет в граф составные пункты назначения, NavGraphBuilder.fragment() добавляет пункт назначения фрагмента.

Подробнее о том, как использовать Kotlin DSL, рассказывается в статье Как создать граф с помощью NavGraphBuilder DSL.

XML

Вы можете написать XML-код самостоятельно. Приведенный ниже пример аналогичен примеру с двумя экранами из предыдущего раздела.

Сначала создайте NavHostFragment. Это хост навигации, который содержит фактический граф навигации.

Минимальная реализация NavHostFragment:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <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" />

</FrameLayout>

Объект NavHostFragment содержит атрибут app:navGraph. Используйте этот атрибут, чтобы подключить граф навигации к хосту навигации. Ниже приведен пример реализации графа.

<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/profile">

    <fragment
        android:id="@+id/profile"
        android:name="com.example.ProfileFragment"
        android:label="Profile">

        <!-- Action to navigate from Profile to Friends List. -->
        <action
            android:id="@+id/action_profile_to_friendslist"
            app:destination="@id/friendslist" />
    </fragment>

    <fragment
        android:id="@+id/friendslist"
        android:name="com.example.FriendsListFragment"
        android:label="Friends List" />

    <!-- Add other fragment destinations similarly. -->
</navigation>

Действия позволяют определить связи между разными целевыми сервисами. В этом примере фрагмент profile содержит действие, которое перенаправляет на friendslist. Подробнее о том, как использовать действия и фрагменты навигации…

Редактор

Управлять графом навигации приложения можно с помощью редактора навигации в Android Studio. По сути, это графический интерфейс пользователя, который можно использовать для создания и редактирования XML-файла NavigationFragment, как описано в предыдущем разделе.

Подробнее о редакторе навигации…

Вложенные графики

Вы также можете использовать вложенные графы. Это предполагает использование графа в качестве пункта назначения навигации. Подробнее о вложенных графах…

Дополнительные материалы

Дополнительную информацию о навигации можно найти в следующих руководствах: