当使用导航组件时关闭DialogFragment的正确方法是什么?

17
我正在使用导航组件来展示一个DialogFragment(在navigation.xml中的<dialog...>...</dialog>),并想知道关闭对话框的推荐方式。我尝试了以下方法并得到以下结果:
1)DialogFragment中的dismiss():似乎可以正常工作。
2)findNavController().navigateUp():似乎可以正常工作。
3)findNavController().navigate(MyDialogFragmentDirections.actionMyDialogFragmentToMyNormalFragment()):可以工作,但会加载目标目的地的新版本,因此根据用例的不同,这可能不是所需的行为。
注意:我的用例是MyNormalFragment使用MyDialogFragment获取一些输入,因此在显示MyDialogFragment后,我需要返回到已经存在的MyNormalFragment实例。
对于我来说,只有 1)或 2)是正确的。现在我想知道,1)和 2)之间有什么区别吗?
1个回答

36

1)和2)最终都会完成相同的任务,但2)始终是更安全的选项。

当您调用dismiss()时,将关闭DialogFragment并停止DialogFragment(它会接收到一个回调方法onStop())。这会触发DialogFragmentNavigator中的监听器,然后通过调用popBackStack()更新NavController的状态。

dismiss()是一种异步操作(在DialogFragment源代码中可以看到, 注意它没有使用commitNow()等方法)。因此,如果您想要从NavController.getCurrentDestination()检查当前所在的目标位置,则会发现您仍然处于对话框目标位置,尽管已经触发了关闭对话框的操作。

navigateUp() 直接前往导航控制器。由于您的后退栈中有另一个目标(DialogFragment下面的一个目标),因此 NavController源代码 显示,navigateUp() 只是调用了 popBackStack() - 同样操作 dismiss() 最终会触发。

然而,当 NavController 在驱动操作时,NavController 会同步更新其状态。这意味着在调用 navigateUp() 立即之后,它将已经更新了其 getCurrentDestination() 和内部状态,除了调用通过 DialogFragmentNavigatorpopBackStack(),这也是调用 dismiss() 的原因(移除上述观察者以防止重复解除)。

因此,调用 navigateUp() 总是更安全的选择,因为它确保 NavController 同步更新到正确的状态,而不是依赖于 FragmentManager 异步的定时(这可能意味着在此期间由于多点触控等原因会收到其他点击事件)。

如果使用一个带有 app:destination 的操作来调用 navigate(),则会导航到目标的新实例,这对于返回到之前的实例来说是不合适的。


对我来说,dismiss(解散)无法正常工作,navhostfragment的当前目标未更新。我想知道如果我的dialog fragment是从navigation graph中启动的,并且根据某些条件而不是基于nav graph,则我是否可以调用findNavController().navigateUp()? - Himanshu
@Himanshu 我有同样的问题,很难处理。 - Pedro Paulo Amorim
所以,如果我有一个取消和保存按钮,并且在对话框外部触摸时有一个忽略事件,那么我应该在哪里调用navigateUp呢? 我目前的问题是,这些操作中的任何一个都会删除我的工具栏上的返回按钮。 - Chucky
@Chucky - 假设您的取消和保存按钮来自 AlertDialog,那么这些按钮已经内部解除对话框,就像点击对话框外部一样。导航仍然监听这些事件并为您更新导航状态。如果您在取消/保存按钮中使用 navigate(),我会确保该操作也弹出对话框(即通过使用 popUpTo)- 这将确保先记录对话框弹出,然后再导航到新目标。 - ianhanniballake
谢谢Hannibal。我怀疑这已经处理过了。但我仍然不确定为什么dismiss会使得下面的片段返回按钮消失? - Chucky

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