Как построить маршрут до пункта назначения

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

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

Использование NavController

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

Независимо от того, какой фреймворк пользовательского интерфейса вы используете, для перехода к пункту назначения можно использовать одну функцию: NavController.navigate().

Для navigate() доступно много перегрузок. Перегрузка, которую вы должны выбрать, соответствует вашему точному контексту. Например, одна перегрузка используется при переходе к composable-функции, а другая – при переходе к представлению.

Ниже описаны некоторые основные перегрузки navigate(), которые вы можете использовать.

Как перейти к composable-функции

Для перехода к composable-функции используйте NavController.navigate<T>. При такой перегрузке navigate() принимает один аргумент route, для которого вы передаете тип. Он служит ключом к пункту назначения.

@Serializable
object FriendsList

navController.navigate(route = FriendsList)

Чтобы перейти к composable-функции в графе навигации, сначала определите NavGraph так, чтобы каждый пункт назначения соответствовал типу. Для composable-функций это делается с помощью функции composable().

Как передавать события из composable-функций

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

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

Пример приведен в следующем подразделе.

Пример

В качестве демонстрации предыдущих разделов обратите внимание на эти моменты в следующем фрагменте:

  1. Каждый пункт назначения в графе создается с использованием маршрута, который представляет собой сериализуемый объект или класс, описывающий данные, необходимые для этого пункта назначения.
  2. Composable-функция MyAppNavHost содержит экземпляр NavController.
  3. Соответственно, вызовы navigate() должны происходить там, а не в composable-функции более низкого уровня, например ProfileScreen.
  4. ProfileScreen содержит кнопку, при нажатии на которую пользователь переходит на страницу FriendsList. Однако он не вызывает navigate().
  5. Вместо этого кнопка вызывает функцию, которая передается в параметре onNavigateToFriends.
  6. Когда MyAppNavHost добавляет ProfileScreen в граф навигации, для onNavigateToFriends он передает лямбду, которая вызывает navigate(route = FriendsList).
  7. Это гарантирует, что при нажатии кнопки ProfileScreen пользователь перейдет к FriendsListScreen.
@Serializable
object Profile
@Serializable
object FriendsList

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = Profile
    ) {
        composable<Profile> {
            ProfileScreen(
                onNavigateToFriends = { navController.navigate(route = FriendsList) },
                /*...*/
            )
        }
        composable<FriendsList> { FriendsListScreen(/*...*/) }
    }
}

@Composable
fun ProfileScreen(
    onNavigateToFriends: () -> Unit,
    /*...*/
) {
    /*...*/
    Button(onClick = onNavigateToFriends) {
        Text(text = "See friends list")
    }
}

Переход по целочисленному идентификатору

Чтобы перейти к пункту назначения, используя целочисленный идентификатор, вызовите перегрузку navigate(int). В качестве значения используется идентификатор ресурса действия или целевой страницы. В следующем фрагменте кода показано, как использовать эту перегрузку для перехода к ViewTransactionsFragment:

Kotlin

viewTransactionsButton.setOnClickListener { view ->
  view.findNavController().navigate(R.id.viewTransactionsAction)
}

Java

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
      Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
  }
});

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

Как перейти из Compose в приложение на основе фрагментов

Если вы используете Jetpack Compose в архитектуре Navigation на основе Fragment (например, в приложении со смешанными представлениями и Compose), вы не можете использовать типобезопасные маршруты Compose для прямой навигации. Вместо этого используйте целочисленные идентификаторы (действия), определенные в XML-графе навигации.

Чтобы изменить пункты назначения из кода Compose, предоставьте событие (лямбда) из вашей composable-функции:

@Composable
fun MyScreen(onNavigateToProfile: () -> Unit) {
    Button(onClick = { onNavigateToProfile() }) {
        Text("Go to Profile")
    }
}

В своем фрагменте свяжите Compose и компонент Navigation на основе фрагментов, вызвав NavController фрагмента при запуске события:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    return ComposeView(requireContext()).apply {
        setContent {
            MyScreen(onNavigateToProfile = { findNavController().navigate(R.id.nav_profile) })
        }
    }
}

Этот подход позволяет повторно использовать и тестировать composable-функции, поскольку они не зависят напрямую от NavController фрагмента.

Навигация с помощью NavDeepLinkRequest

Чтобы перейти к неявной ссылке на контент, используйте перегрузку navigate(NavDeepLinkRequest). Ниже приведен пример реализации этого метода.

Kotlin

val request = NavDeepLinkRequest.Builder
  .fromUri("android-app://androidx.navigation.app/profile".toUri())
  .build()
findNavController().navigate(request)

Java

NavDeepLinkRequest request = NavDeepLinkRequest.Builder
  .fromUri(Uri.parse("android-app://androidx.navigation.app/profile"))
  .build()
NavHostFragment.findNavController(this).navigate(request)

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

Действия и MIME-типы

Помимо Uri, NavDeepLinkRequest также поддерживает ссылки на контент с действиями и MIME-типами. Чтобы добавить действие в запрос, используйте fromAction() или setAction(). Чтобы добавить MIME-тип в запрос, используйте fromMimeType() или setMimeType().

Чтобы NavDeepLinkRequest правильно сопоставлялся с целевым объектом неявной ссылки на контент, URI, действие и MIME-тип должны соответствовать NavDeepLink в целевом объекте. URI должны соответствовать шаблону, действия должны точно совпадать, а MIME-типы должны быть связаны. Например, image/jpg соответствует image/\*.

Дополнительные контексты

В этом документе рассказывается, как использовать NavController.navigate() в наиболее распространенных случаях. Однако у функции есть ряд перегрузок, которые можно использовать в разных контекстах и в тандеме с любым фреймворком пользовательского интерфейса. Дополнительную информацию об этих перегрузках можно найти в справочной документации.

Дополнительные ресурсы

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