Компонент навигации: перейти к пункту назначения Dynami c root и очистить backstack - PullRequest
0 голосов
/ 06 марта 2020

При использовании Jetpack Navigation мы можем использовать popUpTo и popInclusive для очистки стека. Но как мне очистить стек, когда я не знаю, какой пункт назначения popUpto?

Пример

Скажем, у меня есть 3 основных пункта назначения, которые должны иметь чистый стек, когда мы приедем к ним , И у каждого основного пункта назначения есть свой собственный поток экранов (доступ к которому можно получить напрямую с помощью глубокой ссылки).

При условии, что здесь навигация идет вниз:

Start app:
 - Main Dest 1 (nav to dest 2)
     - Dest 2 (nav to dest 3)
         - Dest 3 (nav to main dest 2)
            --clear--
 - (with clear stack)
   Main Dest 2 (nav to dest 4)
     - Dest 4 (nav to main dest 3)
         --clear-->
 - (with clear stack)
   Main Dest 3
 - Back button should close the app here

Поскольку навигация очень динамична c, я не могу гарантировать пункт назначения root. Несмотря на то, что я мог, например, всплыть в «Main Dest 2», я не могу знать, что он находится в стеке, поскольку я мог перейти непосредственно к «dest 4» из URL.

Я нашел способ зная, какой пункт назначения самый низкий, чтобы я мог открыть его и очистить стек?

Ответы [ 4 ]

1 голос
/ 12 марта 2020

Я не смог найти никакого официального решения проблемы, но вот подход, который работает для меня:

private val mBackStackField by lazy {
  val field = NavController::class.java.getDeclaredField("mBackStack")
  field.isAccessible = true
  field
}

fun popToRoot(navController: NavController) {
  val arrayDeque = mBackStackField.get(navController) as java.util.ArrayDeque<NavBackStackEntry>
  val graph = arrayDeque.first.destination as NavGraph
  val rootDestinationId = graph.startDestination

  val navOptions = NavOptions.Builder()
    .setPopUpTo(rootDestinationId, false)
    .build()

  navController.navigate(rootDestinationId, null, navOptions)
}
0 голосов
/ 14 марта 2020

root вашего графика всегда находится в заднем стеке, поэтому вы всегда можете popUpTo это место назначения.

Так что просто убедитесь, что ваш элемент root <navigation> имеет android:id связанный с ним:

<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/main_dest_1">
    <!-- The rest of your graph -->
</navigation>

Затем, когда вы захотите вытолкнуть весь график, просто popUpTo="@id/nav_graph".

Конечно, согласно Принципам навигации , Вы должны не отбрасывать весь стек, когда вы go отправляетесь в Main Dest 2, Main Dest 3, et c. так как с точки зрения UX очень важно, чтобы пользователи знали, когда кнопка «Назад» выйдет из приложения, - именно это место назначения является именно той подписью, чтобы пользователи знали, когда это произойдет, и предотвращает случайное закрытие приложения, когда пользователи ожидают возврата до первого экрана перед выходом из приложения.

0 голосов
/ 13 марта 2020

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

NavController.OnDestinationChangedListener { controller, destination, arguments ->
    // react on change or 
    //you can check destination.id or destination.label and act based on tha
)}
0 голосов
/ 09 марта 2020

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


Использовать некоторый базовый фрагмент

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

<action
    android:id="..."
    app:destination="@id/mainDestX"
    app:popUpTo="@+id/baseDest"
    app:popUpToInclusive="false" />

. Это всегда будет держать ваш baseDest в нижней части вашего стека, позволяя вам всегда открывать его. Чтобы добиться своего поведения при отжиме в одном из ваших основных пунктов назначения, вы можете:

  1. Переопределить метод onBackPressed во всех ваших основных окнах, чтобы закрыть приложение. (Это может быть легко сделано, если все ваши основные определения выходят из одного и того же класса)
  2. Когда ваш фрагмент baseDest загружен из-за обратной печати, закройте приложение программным способом.

Переосмыслить Навигация в вашем приложении

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


Найти решение программно

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

  1. Выполнить yourNavController.getGraph().clear() с последующим yourNavController.navigate(yourDest)
  2. В начале вашего приложения перед любым фрагментом загружается с помощью saved = yourNavController.saveState() и при переходе к одному из ваших основных назначений используйте yourNavController.restoreState(saved), затем снова следует yourNavController.navigate(yourDest)
  3. Создайте новый NavGraph и используйте yourNavController.setGraph(createdGraph)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...