在导航到DialogFragment之前检查当前的导航目标吗?

时间:2019-05-31 09:32:22

标签: android android-livedata android-navigation android-viewmodel

我有一个简单的项目来测试NavigationDialogFragment目的地如何工作:

enter image description here

一切正常, ,如果我尝试快速连续打开/关闭对话框。如果点击得足够快,我会得到一个

  

IllegalArgumentException:导航目标   NavController未知com.example.app:id/show_dialog

很明显,这是一个极端情况,因为大多数用户除非尝试测试应用程序的完整性,否则不会快速重复按下按钮。

无论如何,要解决此问题,我添加了一项检查以确保当前目的地是包含show_dialog操作的目的地。

val currentDest = findNavController().currentDestination?.id
if (currentDest == R.id.fragment_dest) {
    findNavController().navigate(TestFragmentDirections.showDialog())
}

进行此更改似乎已消除了该问题。无论我按下按钮有多快,我都无法重现该错误。到目前为止,这已经足够了,但是我想知道其背后的原因。

  

我的问题:

     

在这种情况下,为什么必须用条件语句包装navigate调用?

我的猜测是,它与1)观察者模式或2)与创建/显示对话框相关的滞后有关。或两者结合。

无论如何,这里是相关代码:

TestFragment.kt

class TestFragment : Fragment() {

    private lateinit var binding: TestFragmentBinding
    private lateinit var viewModel: TestViewModel

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {

        binding = TestFragmentBinding.inflate(inflater, container, false)
        viewModel = ViewModelProviders.of(this).get(TestViewModel::class.java)

        binding.lifecycleOwner = this
        binding.viewModel = viewModel

        viewModel.eventShowDialog.observe(this, Observer { showDialog ->
            if (showDialog) {

                /**
                 * Error occurs here without the if statement
                 */
                val currentDest = findNavController().currentDestination?.id
                if (currentDest == R.id.fragment_dest) {
                    findNavController().navigate(TestFragmentDirections.showDialog())
                }

                viewModel.onShowDialogComplete()
            }
        })

        return binding.root
    }
}

TestViewModel.kt

class TestViewModel : ViewModel() {

    private val _eventShowDialog = MutableLiveData<Boolean>()
    val eventShowDialog: LiveData<Boolean>
        get() = _eventShowDialog

    fun onShowDialog() {
        _eventShowDialog.value = true
    }

    fun onShowDialogComplete() {
        _eventShowDialog.value = false
    }
}

dialog_nav.xml

<navigation
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/dialog_nav"
    app:startDestination="@id/fragment_dest">

    <fragment
        android:id="@+id/fragment_dest"
        android:name="com.example.app.TestFragment"
        android:label="Fragment">

        <action
            android:id="@+id/show_dialog"
            app:destination="@id/dialog_dest"/>

    </fragment>

    <dialog
        android:id="@+id/dialog_dest"
        android:name="com.example.app.TestDialogFragment"
        android:label="Dialog"/>

</navigation>

fragment_test.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewModel"
            type="com.example.app.TestViewModel"/>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:id="@+id/dialog_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Show Dialog"
            android:onClick="@{() -> viewModel.onShowDialog()}"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

还有一张很好的图片。快速连续点击“显示对话框”按钮会产生错误。

enter image description here

2 个答案:

答案 0 :(得分:1)

Your question is just based on the Android inside Architecture and is also dependent on the hardware performance. Just wrap it in a try/catch block:

try{
  findNavController().navigate(TestFragmentDirections.showDialog())
}catch(e: IllegalArgumentException){
    e.printStackTrace
}

答案 1 :(得分:0)

您可能会得到IllegalArgumentException,因为如果看到showDialog()方法,则会发现实现如下:

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}

我建议您编写具有以下实现方式的showDialog方法:

fragmentManager.beginTransaction()
               .add(dialog, "TAG")
               .commitAllowingStateLoss();