我开始学习共享视图模型。 目前,我在活动中有3个片段,其中2个是在嵌套的navGraph中。
我想为他们两个创建共享的navGraph viewModel范围,但是我不明白如何以及在何处初始化这些片段中的视图模型。
在过去的所有应用程序中,我都创建了全局视图模型
private lateinit var viewModel: MainViewModel
然后在onCreateView
中,我像这样初始化viewModel-
viewModel = ViewModelProvider(this, Factory(requireActivity().application)).get(
MainViewModel::class.java)
如果要共享一个包含2个片段的视图模型,如何对navGraph viewModel范围进行相同操作?
目前我有这种方法:
private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)
可以,但是
A。我从未见过viewModel在全局变量中正确使用
B 。我无法通过这种方法在工厂内部传递变量
答案 0 :(得分:2)
private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)
这是可行的,但是
A。我从未见过在全局变量中正确初始化过viewModel
B。我无法使用这种方法在工厂内部传递变量
A。)在这种情况下,ViewModel会在首次访问时初始化,因此,如果您仅在homeViewModel
或onCreate
中输入onViewCreated
,则会在正确的范围内创建它。
B。)这是事实,您可以使用带有navGraphViewModels
的自定义工厂,但是您真正想要的(可能)是隐式地将任何Fragment参数传递给ViewModel(请注意,两个您的片段必须在其参数中具有正确的键,这样才能安全地工作)(使用SavedStateHandle
。
要获取SavedStateHandle
,您需要使用AbstractSavedStateViewModelFactory
。要创建一个,必须在onViewCreated
内创建ViewModel(onCreate
不能与navgraphs一起使用),这最简单的方法是使用ViewModelLazy
。
要创建viewModelLazy
,您可以使用createViewModelLazy
(在屏幕上显示的内容)。这可以定义一种传递ViewModelStoreOwner
(即NavBackStackEntry
)和SavedStateRegistryOwner
(也即NavBackStackEntry
)的方式。
因此您可以将其放入代码中,并且应该可以使用。
inline fun <reified T : ViewModel> SavedStateRegistryOwner.createAbstractSavedStateViewModelFactory(
arguments: Bundle,
crossinline creator: (SavedStateHandle) -> T
): ViewModelProvider.Factory {
return object : AbstractSavedStateViewModelFactory(this, arguments) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(
key: String, modelClass: Class<T>, handle: SavedStateHandle
): T = creator(handle) as T
}
}
inline fun <reified T : ViewModel> Fragment.navGraphSavedStateViewModels(
@IdRes navGraphId: Int,
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
// Wrapped in lazy to not search the NavController each time we want the backStackEntry
val backStackEntry by lazy { findNavController().getBackStackEntry(navGraphId) }
return createViewModelLazy(T::class, storeProducer = {
backStackEntry.viewModelStore
}, factoryProducer = {
backStackEntry.createAbstractSavedStateViewModelFactory(
arguments = backStackEntry.arguments ?: Bundle(), creator = creator
)
})
}
inline fun <reified T : ViewModel> Fragment.fragmentSavedStateViewModels(
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
return createViewModelLazy(T::class, storeProducer = {
viewModelStore
}, factoryProducer = {
createAbstractSavedStateViewModelFactory(arguments ?: Bundle(), creator)
})
}
@Suppress("UNCHECKED_CAST")
inline fun <reified T : ViewModel> Fragment.fragmentViewModels(
crossinline creator: () -> T
): Lazy<T> {
return createViewModelLazy(T::class, storeProducer = {
viewModelStore
}, factoryProducer = {
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(
modelClass: Class<T>
): T = creator.invoke() as T
}
})
}
现在您可以做
private val homeViewModel: HomeViewModel by navGraphSavedStateViewModels(R.id.nested_navigation) { savedStateHandle ->
HomeViewModel(savedStateHandle)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view)
homeViewModel.someData.observe(viewLifecycleOwner) { someData ->
...
}
}