我正在使用导航组件来启动和关闭ErrorDialog。我注意到,当我尝试重新打开重试流上的错误对话框时,我得到一个错误,即无法找到该操作。它在对话框第一次启动时起作用。
findNavController().navigate(R.id.action_secondFragment_to_dialog_navigation)
我还注意到我必须按两次后退按钮才能返回到原始页面。我认为问题在于DialogFragment没有作为当前目的地被删除.我是对的。
当我第二次使用调试检查器时,您想要启动它,它显示当前导航目的地是对话框片段。
使用一个带有小延迟的延迟& postDelayed来解决问题。仅仅在主线程上使用帖子是不起作用的;你需要一个小的延迟。
然而,这似乎不是正确的解决方案。这真的是一个比赛条件还是我做错了什么?
下面是相关代码。
这里是我从第二个片段中调度DialogFragment的地方
private fun showErrorDialog() {
if (!isRemoving && isVisible) {
findNavController().navigate(R.id.action_secondFragment_to_dialog_navigation)
}
}
这里是我设置结果并退出对话框的地方(在ErrorDialogFragment中)
private fun navigateBackWithResult(result: Boolean) {
findNavController()
.previousBackStackEntry
?.savedStateHandle
?.set(DIALOG_RESULT, result)
}
这里是我从ErrorDialog中监听结果的地方
private fun setupErrorDialogListener() {
findNavController()
.currentBackStackEntry
?.savedStateHandle
?.getLiveData<Boolean>(ErrorDialogFragment.DIALOG_RESULT)
?.observe(viewLifecycleOwner) {
Log.d("dialog returned in observer with $it", TAG)
it?.let {
//Solves an issue with navigation component after retrying dialog. My guess
//is that you need to wait for error dialog fragment transaction to complete and be removed from backstack
//Does not work without the small delay.
Handler(Looper.getMainLooper()).postDelayed(
{
if (it) binding.vm?.refreshCalled() else Log.d("user declined to refresh", TAG)
},
50,
)
}.whenNull { Log.e("failed to get user response from fragment through navigator", TAG) }
}
}
下面是启动对话框的片段的导航XML
<fragment
android:id="@+id/secondFragment"
android:name="ca.xyz.android.fragments.secondFragment"
android:label="@string/second_fragment"
tools:layout="@layout/second_fragment" />
<action
android:id="@+id/action_secondFragment_to_dialog_navigation"
app:destination="@id/dialog_navigation"
app:launchSingleTop="false"
app:popUpTo="@id/secondFragment">
<argument
android:name="DIALOG_RESULT"
app:argType="boolean" />
</action>
</fragment>
(我不认为singleTop标志有任何作用,我会删除它,看看会发生什么,但这是不相关的。
下面是对话框片段的相关导航XML
<navigation android:id="@+id/dialog_navigation"
app:startDestination="@id/error_dialog">
<dialog
android:id="@+id/error_dialog"
android:label="@string/step_two_title"
android:name="ca.xyz.android.fragments.ErrorDialogFragment">
<argument
android:name="DIALOG_RESULT"
app:argType="boolean" />
</dialog>
</navigation>
1条答案
按热度按时间2ul0zpep1#
当你使用这种回调函数时(也就是
currentBackStackEntry
和previousBackStackEntry
作为活动数据),如果对话框是打开的,在前面的片段中,viewLifecycleOwner
仍然是活动的。这意味着,当您设置结果时,它会同步传输,这意味着错误对话框仍然可见。动作action_secondFragment_to_dialog_navigation
必须在secondFragment
位于前台时从secondFragment
调用,也就是在它位于顶部片段时。如果它被认为是非活动的,则会引发此异常。Handler().postDelayed()
方法之所以有效,是因为您给了给予时间让错误对话框消失到第二个片段,由于导航更改,第二个片段成为顶部片段,从而允许它再次触发错误片段。简单的post
不起作用,因为它可能需要多个帧才能使片段完全消失(考虑到动画等),或者将其注册到导航器中。这可以被认为是一个竞争条件(但不完全是因为它不是两个线程,而是两个独立的片段/对话框),你试图在对话框消失之前再次调用它。
您可以:
navigate(R.id.error_dialog)
),这将不会使用该操作并解决崩溃问题(这可能会造成一个小的内存泄漏,请小心),navigator.addOnDestinationChangedListener
并检测它何时被添加,之后,再次调用对话框。