Android导航对话框片段回调

时间:2020-05-13 16:37:27

标签: android mvvm android-dialogfragment android-jetpack

我的项目(MVVM,Jetpack导航)中有DialogFragment,它从不同的地方调用并代表签名画布。导航相关部分:

 <dialog
        android:id="@+id/signPadDialogFragment"
        android:name="com.ui.signpad.SignPadDialogFragment"
        android:label="SignPadDialogFragment" />

 <fragment
        android:id="@+id/loginFragment"
        android:name="com.ui.login.LoginFragment"
        android:label="@string/login_label"
        tools:layout="@layout/login_fragment">

        <action
            android:id="@+id/action_loginFragment_to_currentJobsFragment"
            app:destination="@id/currentJobsFragment" />
        <action
            android:id="@+id/action_loginFragment_to_signPadDialogFragment"
            app:destination="@id/signPadDialogFragment" />

<fragment
        android:id="@+id/jobDetailFragment"
        android:name="com.ui.jobdetails.JobDetailFragment"
        android:label="job_detail_fragment"
        tools:layout="@layout/job_detail_fragment" >
        <action
            android:id="@+id/action_jobDetailFragment_to_signPadDialogFragment"
            app:destination="@id/signPadDialogFragment" />
    </fragment>

并导航操作:

 mainActivityViewModel.repository.navigationCommands.observe(this, Observer { navEvent ->
            navEvent.getContentIfNotHandled()?.let {
                navController.navigate(it as NavDirections)
            }

        })

所以,我的问题是:使用Jetpack导航和MVVM处理回调的正确方法是什么? 我看到两个可能的解决方案和相关问题:

我可以将数据从对话框片段传递到ViewModel->存储库(在这种情况下:如何区别在对话框作用域内启动对话框的操作?

或者在MainActivity中获得回调(如何?)

预先感谢

1 个答案:

答案 0 :(得分:0)

由于 NavController API 的局限性,因此只能从存在Android context 的元素中发现。这意味着您的主要选择是:

  1. AndroidViewModel:我不推荐这样做,因为在这里很容易被上下文所干扰,如果您不知道自己在做什么,则会导致内存泄漏。
  2. 活动:处理活动中的导航。单一活动架构会使事情复杂化,因为您必须在这里限制所有导航逻辑。
  3. 片段:处理其范围内每个片段的导航。这样更好,但是下面有一个更好的解决方案。
  4. ViewModel:在范围限定于其的viewModel中处理每个片段的导航。 (个人偏好)。

在Jetpack导航组件中使用ViewModel

从技术上讲,导航登录名仍将保留在 Fragment 中,除非navigation API进行了更改,否则无法逃避,但是我们可以将主要部分委托给 {{1 }} 如下:

  1. ViewModel将显示一个 ViewModel ,其中包含一个 SingleLiveEvent NavDirection 是仅触发一次的实时数据,这是我们在导航时需要的。何塞·阿尔塞雷卡(JoseAlcérreca)撰写了一篇很棒的博客文章:

    https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150

  2. SingleLiveEvent 将遵守此 Fragment 并将使用该 SingleLiveEvent 进行导航交易。

暴露SingleLiveEvent的ViewModel:

NavDirection

从ViewModel观察此SingleLiveEvent的片段:

open class BaseViewModel : ViewModel() {
  /**
   * Navigation Component API allows working with NavController in the Following:
   * 1.) Fragment
   * 2.) Activity
   * 3.) View
   *
   * In order to delegate the navigation logic to a viewModel and allow fragment
   * or an activity to communicate with viewModel, we expose navigationCommands
   * as LiveData that contains Event<NavigationCommand> value.
   *
   * Event<T> is simply a wrapper class that will only expose T if it has not
   * already been accessed with the help of a Boolean flag.
   *
   * NavigationCommand is a Sealed class which creates a navigation hierarchy
   * where child classes can take NavDirections as properties. We will observe the
   * value of NavigationCommand in the fragment and pull the NavDirections there.
   */
  private val _navigationCommands = MutableLiveData<Event<NavigationCommand>>()
  val navigationCommands: LiveData<Event<NavigationCommand>>
    get() = _navigationCommands

  fun navigate(directions: NavDirections) {
    _navigationCommands.postValue(Event(NavigationCommand.To(directions)))
  }
}

您可以使用 private fun setupNavigation() { viewModel.navigationCommands.observe(viewLifecycleOwner, Observer { val navigationCommand = it.getContentIfNotHandled() when (navigationCommand) { is NavigationCommand.To -> { findNavController().navigate(navigationCommand.directions) } } }) } BaseFragment 来跟踪 BaseViewModels ,但始终可以请记住,任何以 DRY 作为前缀的东西都会很快变成代码的味道,因此请保持简洁。