导航组件:跳转到动态根目录并清除后退堆栈

问题描述 投票:0回答:4

[使用Jetpack导航时,我们可以使用popUpTopopInclusive清除堆栈。但是当我不知道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

由于导航非常动态,因此我无法保证根目录的目的地。例如,即使我可以弹出“ Main Dest 2”,我也不知道它在堆栈中,因为我可能直接从URL转到“ Dest 4”。

我有办法知道哪个目的地是最低的,所以我可以弹出它并清除堆栈?

android kotlin android-jetpack android-navigation android-jetpack-navigation
4个回答
0
投票

据我所知,没有简单而优美的方法来获得您想要达到的目标。不过,有一些可能的解决方案。


使用某种基本片段

您可能有一个始终位于堆栈底部的基本目标,我们称它为baseDest。在需要清除堆栈的操作中,您可以随时使用

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

这将始终将baseDest保留在堆栈的底部,使您始终可以弹出它。要在您的主要目的地之一上实现反压行为,您可以:

  1. 在所有主要目标中覆盖onBackPressed方法以关闭应用程序。 (如果您的所有主要目标都来自同一类,则可以轻松完成)
  2. 当由于反压而加载baseDest片段时,以编程方式关闭应用程序。

重新考虑您的应用导航

也许您应该重新考虑应用程序中导航的工作方式。与其具有某种可能的循环图结构,不如处理树状导航结构(用户也可以更直观地理解它们)。当然,这并不总是一种选择,但是您应该牢记这一点,并考虑重新构造导航。


以编程方式找到解决方案

手动执行所有操作和导航,并使用NavController中提供的方法,在导航到您的主要目的地之一之前,手动删除堆栈中的所有条目。为此,一种可行的方法是使用片段中的findNavController方法来检索导航控制器。我尚未测试以下任何一种方法,但是您可以尝试:

  1. 先执行yourNavController.getGraph().clear(),然后再执行yourNavController.navigate(yourDest)
  2. 在加载任何片段之前,在应用程序的开头使用saved = yourNavController.saveState(),并在导航到您的mainDestinations之一时使用yourNavController.restoreState(saved),再次在后跟yourNavController.navigate(yourDest)
  3. 创建新的NavGraph并使用yourNavController.setGraph(createdGraph)

0
投票

我找不到解决该问题的任何官方解决方案,但这是一种对我有用的方法:

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
投票

您可以在根或基本片段中添加目标更改侦听器,并侦听导航更改。

NavController.OnDestinationChangedListener { controller, destination, arguments ->
    // react on change or 
    //you can check destination.id or destination.label and act based on tha
)}

0
投票

图形的根始终在后堆栈上,因此您始终可以popUpTo该目标。

因此只需确保您的根<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"

当然,根据Principles of Navigation,当您转到Main Dest 2,Main Dest 3等时,应该not将整个堆栈弹出。因为从UX角度来看,用户极为重要知道后退按钮何时退出应用程序-启动目的地正是该路标,供用户知道何时会发生,并防止在用户希望退出应用程序前返回第一个屏幕时意外关闭应用程序。

© www.soinside.com 2019 - 2024. All rights reserved.