【问题标题】:Android Navigation Dialog Fragment callbackAndroid 导航对话框片段回调
【发布时间】:2020-05-13 16:37:27
【问题描述】:

我的项目(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 中获取回调(如何?)

提前致谢

【问题讨论】:

    标签: android mvvm android-dialogfragment android-jetpack


    【解决方案1】:

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

    1. AndroidViewModel:我不推荐它,因为这里很容易被上下文迷住,如果你不知道自己在做什么,这会导致内存泄漏。
    2. Activity:处理 Activity 中的导航。单 Activity 架构会使事情复杂化,因为您必须在此处压缩所有导航逻辑。
    3. 片段:处理其范围内每个片段的导航。这要好得多,但下面有一个更好的解决方案。
    4. ViewModel:在其作用域的 viewModel 中处理每个片段的导航。 (个人喜好)。

    在 Jetpack 导航组件中使用 ViewModel

    技术上导航登录仍将驻留在Fragment,除非navigation API发生变化,否则我们可以将主要部分委托给ViewModel如下:

    1. ViewModel 将公开一个 SingleLiveEvent,其中封装了一个 NavDirectionSingleLiveEvent 是一个只触发一次的实时数据,这正是我们在导航时想要的。 Jose Alcérreca 有一篇很棒的博文:

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

    2. Fragment 将观察到此 SingleLiveEvent 并将使用该 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)))
      }
    }
    

    从 ViewModel 观察此 SingleLiveEvent 的片段:

      private fun setupNavigation() {
        viewModel.navigationCommands.observe(viewLifecycleOwner, Observer {
          val navigationCommand = it.getContentIfNotHandled()
    
          when (navigationCommand) {
            is NavigationCommand.To -> { findNavController().navigate(navigationCommand.directions) }
          }
        })
      }
    

    您可以使用 BaseFragmentBaseViewModels 来关注 DRY,但请记住,任何具有 Base 作为前缀很快就会变成代码异味,所以尽量保持简洁。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-02
      • 2018-03-04
      相关资源
      最近更新 更多