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() для завершения работы активности. Следующий фрагмент кода демонстрирует это:
котлин
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
java
...
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 .
Пример 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"/>
Пример составления текста
Ниже приведён полный пример того же самого в 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() см. в руководстве по навигации с использованием параметров .
Вывод с помощью действий
При навигации с помощью действия вы можете дополнительно удалить дополнительные элементы из стека «Назад». Например, если ваше приложение имеет начальный процесс авторизации, после того, как пользователь войдет в систему, следует удалить все элементы, связанные с авторизацией, из стека «Назад», чтобы кнопка «Назад» не возвращала пользователя обратно в процесс авторизации.
Дополнительная литература
Для получения более подробной информации ознакомьтесь со следующими страницами:
- Циклическая навигация : узнайте, как избежать переполнения стека возврата в случаях, когда потоки навигации имеют циклический характер.
- Направления диалогов : Узнайте о том, как направления диалогов вносят уникальные особенности в управление стеком возврата.