Android导航 - 如何深入前往两个目的地?

5
我有一个管理“箱子”的应用程序。 这个应用程序有一个单独的活动,并使用片段作为目标。 它有(除其他外)以下目标/片段:
  • 主页(注册为顶级目标)
  • 箱子管理(注册为顶级目标)
  • 编辑箱子

从主页目标出发,我想提供一个方便的链接来创建一个新的箱子。 为此,我希望直接链接到编辑箱子,但将箱子管理保留在返回栈中。

尝试1-显式深入链接

由于中间目的地只有在它是<navigation>元素的起始目的地时才会落在后堆栈上,因此我将BoxesManagement(作为起始点)和BoxEdit放在一个嵌套图形中。 嵌套图形位于单独的文件中,但在父图形中被<include>引用。 然后我使用了:
findNavController()
    .createDeepLink()
    .setDestination(R.id.nav_edit_box)
    .createPendingIntent()
    .send()

这确实以我想要的方式运行,将BoxesManagement放在后台堆栈中,使我到达了目的地。 但是,它引起了两个问题:
  • 应用程序重新启动。至少我认为是这样,因为整个屏幕会短暂闪烁。这种行为让我认为我不应该使用深层链接进行应用内导航。(这并不奇怪,因为文档从未暗示过这种用例。)
  • 当我返回到BoxesManagement时,我看到汉堡菜单图标(如预期)。但是,当我使用抽屉菜单导航到Home时,什么也没有发生。

尝试2-直接导航

findNavController()
    .navigate(R.id.nav_box_edit)

这个不起作用,只是出现了错误信息:“”。
java.lang.IllegalArgumentException: Navigation action/destination <APP-PKG>:id/nav_box_edit cannot be found from the current destination Destination(<APP-PKG>:id/nav_home) label=Home class=<APP-PKG>.ui.HomeFragment 

我并不感到惊讶,因为来自Google的Murat Yener已经指出这不受支持。

尝试2b - 设置图表

这个问题和答案中,我得到了在导航前设置导航控制器上的图表的想法。

findNavController().apply {
    setGraph(R.navigation.nav_graph_boxes)
    navigate(R.id.nav_box_edit)
}

这确实可以运行,但也会导致两个问题:
  • 在跟随此导航时,导航控制器的图现在是R.navigation.nav_graph_boxes而不是R.navigation.nav_graph。当我使用通过BoxesManagement的单步不同路径时,我也可以到达BoxEdit,但控制器的图为R.navigation.nav_graph
  • 与深度链接的上述问题相同,使用抽屉菜单导航到Home什么都不做。我认为这实际上是第一个问题的直接后果。

链接的Q&A中提出的解决方案是在返回导航时将图设置回R.navigation.nav_graph。然而,我不知道在代码的哪个位置进行操作,因为我没有通过任何显式操作返回导航,而是通过向上按钮和抽屉菜单进行返回导航,而这种方法已经不能工作了。

尝试 3 - 隐式深度链接

根据官方文档的建议,我决定尝试隐式深度链接,尽管这并没有带来使用导航组件时承诺的那种甜蜜类型安全感。
我添加了...
<deepLink app:uri="android-app://my.app.url/boxes/{boxId}/edit"/>

nav_box_edit 片段定义添加并使用。
findNavController()
    .navigate(Uri.parse("android-app://my.app.url/boxes/0/edit"))

从“主页”目的地开始。
这将我带到了我的目的地,但我再次遇到了问题:
  • 它不将“BoxesManagement”放入后退堆栈中(根据文档,这是可预期的)。

问题

我应该如何正确地导航到一个(嵌套的)目标,并在其之前将另一个目标放入后退堆栈中?


如果您想将两个目的地添加到后退栈中,为什么不调用 navigate() 两次呢? - ianhanniballake
起初我以为这行不通(请看下面我的回答),但我解决了。也许在文档中加上一两句话会更好?在我看来,navigate() 的意思是:“现在去这里!不要做其他任何事情。”我错误地假设执行将继续目标位置的生命周期。 - AplusKminus
1个回答

3

深度导航两个目的地

为了按照预期创建后退栈,您只需将所有所需目标推入栈中:

findNavController().apply {
    navigate(R.id.nav_boxes_management)
    navigate(R.id.nav_box_edit)
}

仅当所有目的地都在同一个导航图中时,此方法才有效。如问题所述,这里情况并非如此,因此需要采用稍微不同的方法:

findNavController().apply {
    navigate(FragmentHomeDirections.actionBoxesManagement())
    navigate(FragmentBoxesManagementDirections.actionEditBox(0L))
}

使用最高层的目标BoxesManagement的方向,成功导航到嵌套图中。

(感谢ianhanniballake提供关键提示!)

返回Home

这样做是起作用了,但我不知道为什么:

findNavController().apply {
    navigate(FragmentHomeDirections.actionBoxesManagement(), navOptions {
        popUpTo(R.id.nav_home) {
            inclusive = false
            saveState = true
        }
    })
    navigate(FragmentBoxesManagementDirections.actionEditBox(0L))
}

我通过查看NavigationUI的源代码弄清楚了问题,该代码有效地处理了抽屉菜单的操作。


当你说“同一个nav_graph”时,是指相同的导航路径还是相同的图形文件?因为我有一些嵌套的图形,但它们都是通过方向的同一路径的一部分。 - htafoya
“同一 nav_graph” 指的是相同的图形文件。如果不是这种情况,第一个代码片段将无法工作,但第二个代码片段可以。 - AplusKminus
嗯,它实际上使用了嵌套图表并成功运行。 - htafoya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接