在我的项目中(MVVM, Jetpack navigation)有一个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 -> Repository(和在这种情况下: 在这种情况下:如何在对话框范围内区分启动对话框的动作?)
或者在MainActivity中得到一个回调(如何?)
先谢谢你
由于 NavController API
的元素中发现,它只能从具有android context
目前。这意味着你的首要选项是。
从技术上讲,导航登录仍将驻留在 Fragment
,没有人可以逃避,除非 navigation API
变化,但我们可以将主要部分委托给 ViewModel
如下所示。
ViewModel将暴露一个 SingleLiveEvent
囊括 NavDirection
中。SingleLiveEvent
是一个只被触发一次的Live Data,这也是我们在导航方面所希望的。Jose Alcérreca有一篇很棒的博文。
Fragment
将此 SingleLiveEvent
并将以此 NavDirection
来执行一个导航事务。
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) }
}
})
}
你可以利用 "SingleLiveEvent: Fragment "来观察这个SingleLiveEvent。BaseFragment
和 BaseViewModels
遵循 DRY
但要永远记住,任何事情,有 Base
作为前缀很快就会变成一种代码味,所以要尽量简洁。