NavController содержит "обратный стек" с пунктами назначения, которые посетил пользователь. Когда пользователь переходит между экранами приложения, NavController добавляет и удаляет пункты назначения из обратного стека.
Обратный стек – это структура данных, работающая по принципу "последним пришел – первым ушел". Поэтому NavController добавляет элементы в начало стека и удаляет их оттуда.
Базовое поведение
Вот основные факты, которые следует учитывать в отношении поведения обратного стека:
- Первый пункт назначения. Когда пользователь открывает приложение,
NavControllerпомещает первый пункт назначения в начало обратного стека. - Добавление в стек. Каждый вызов
NavController.navigate()добавляет указанный пункт назначения в верхнюю часть стека. - Удаление верхнего пункта назначения. При нажатии кнопки Вверх или Назад вызываются методы
NavController.navigateUp()иNavController.popBackStack()соответственно. Они извлекают верхний пункт назначения из стека. Подробнее о разнице между кнопками Вверх и Назад рассказывается в статье Принципы навигации.
Возврат
Метод NavController.popBackStack() пытается извлечь текущий пункт назначения из обратного стека и перейти к предыдущему пункту назначения. Это позволяет пользователю вернуться на один шаг назад в истории навигации. Она возвращает логическое значение, указывающее, удалось ли вернуться к целевому экрану.
Вернуться к определенному пункту назначения
Вы также можете использовать popBackStack() для навигации к определенному пункту назначения. Для этого используйте одну из перегрузок. Некоторые из них позволяют передавать идентификатор, например целое число id или строку route. Эти перегрузки направляют пользователя на целевую страницу, связанную с указанным идентификатором. Важно, что они извлекают из стека все, что находится выше этого пункта назначения.
Эти перегрузки также принимают логическое значение inclusive. Он определяет, должен ли NavController также удалять указанный пункт назначения из обратного стека после перехода к нему.
Рассмотрим следующий пример:
navController.popBackStack(R.id.destinationId, true)
Здесь NavController возвращается к пункту назначения с целочисленным идентификатором destinationId. Поскольку значение аргумента inclusive равно true, NavController также удаляет указанный пункт назначения из обратного стека.
Обработка неудачного возврата
Если popBackStack() возвращает false, то последующий вызов NavController.getCurrentDestination() возвращает null. Это означает, что приложение извлекло последний пункт назначения из обратного стека. В этом случае пользователь видит только
пустой экран.
Это может произойти в следующих случаях:
popBackStack()не извлек ничего из стека.popBackStack()удалил пункт назначения из обратного стека, и теперь стек пуст.
Чтобы решить эту проблему, перейдите на другую страницу или вызовите finish() для завершения действия. Это показано в следующем фрагменте кода:
kotlin
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
Ява
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish();
}
Всплывающее окно с пунктом назначения
Чтобы удалить пункты назначения из обратного стека при переходе от одного пункта назначения к другому, добавьте аргумент popUpTo() в связанный вызов функции navigate(). popUpTo() указывает библиотеке Navigation удалить некоторые пункты назначения из обратного стека в рамках вызова navigate(). Значение параметра – это идентификатор пункта назначения в обратном стеке. Идентификатор может быть целым числом id или строкой route.
Вы можете включить аргумент для параметра inclusive со значением true, чтобы указать, что пункт назначения, указанный в popUpTo(), также должен быть удален из обратного стека.
Чтобы реализовать это программно, передайте popUpTo() в navigate() как часть NavOptions, задав для inclusive значение true. Это работает как в Compose, так и в Views.
Сохранять состояние при всплывании
Когда вы используете popUpTo для перехода к пункту назначения, вы можете сохранить обратный стек и состояния всех пунктов назначения, извлеченных из обратного стека. Затем вы можете восстановить обратный стек и пункты назначения при переходе к этому пункту назначения в более позднее время. Это позволяет сохранить состояние для заданного пункта назначения и иметь несколько обратных стеков.
Чтобы сделать это программно, укажите saveState = true при добавлении popUpTo в параметры навигации.
Вы также можете указать restoreState = true в параметрах навигации, чтобы автоматически восстановить обратный стек и состояние, связанное с пунктом назначения.
Пример:
navController.navigate(
route = route,
navOptions = navOptions {
popUpTo<A>{ saveState = true }
restoreState = true
}
)
Чтобы включить сохранение и восстановление состояния в XML, определите popUpToSaveState как true и restoreState как true в связанном файле action.
Пример запроса
Ниже приведен полный пример того же самого в Compose:
@Composable
fun MyAppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: Any = A
) {
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable<A> {
DestinationA(
onNavigateToB = {
// Pop everything up to, and including, the A destination off
// the back stack, saving the back stack and the state of its
// destinations.
// Then restore any previous back stack state associated with
// the B destination.
// Finally navigate to the B destination.
navController.navigate(route = B) {
popUpTo<A> {
inclusive = true
saveState = true
}
restoreState = true
}
},
)
}
composable<B> { DestinationB(/* ... */) }
}
}
@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
Button(onClick = onNavigateToB) {
Text("Go to A")
}
}
Вы можете изменить способ вызова NavController.navigate() следующими способами:
// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a")
}
// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a") { inclusive = true }
}
// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
launchSingleTop = true
}
Общую информацию о передаче параметров в NavController.navigate() можно найти в руководстве по навигации с параметрами.
Пример (для фидов в формате XML)
Ниже приведен пример использования popUpTo в XML с действием.
<action
android:id="@+id/action_a_to_b"
app:destination="@id/b"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"
app:restoreState=”true”
app:popUpToSaveState="true"/>
Действия с поп-апами
При навигации с помощью действия вы можете дополнительно извлечь из обратного стека другие пункты назначения. Например, если в вашем приложении есть начальный процесс входа в систему, после того как пользователь вошел в систему, вы должны удалить все связанные со входом пункты назначения из обратного стека, чтобы кнопка "Назад" не возвращала пользователей к процессу входа.
Дополнительные сведения
Дополнительную информацию можно найти в следующих статьях:
- Циклическая навигация. Узнайте, как избежать переполнения обратного стека при циклической навигации.
- Целевые страницы диалоговых окон. Узнайте, как целевые страницы диалоговых окон влияют на управление обратным стеком.