如何使用 navGraph 范围初始化 viewModel [英] How to initilaize viewModel using navGraph scope

查看:107
本文介绍了如何使用 navGraph 范围初始化 viewModel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始学习共享视图模型.目前我在 Activity 中有 3 个片段,其中 2 个在嵌套的 navGraph 中.

I'm starting to learn about shared view model. Currently I've 3 fragments inside activity, 2 of them are inside nested navGraph.

我想为它们创建共享的 navGraph viewModel 范围,但我无法理解如何以及在何处初始化这些片段中的视图模型.

I want to create shared navGraph viewModel scope to both of them, but I can't understand how and where I can initilaze the view model inside those fragments.

在我过去的所有应用程序中,我创建了全局视图模型

At all my past apps, I created Global view model

private lateinit var viewModel: MainViewModel

然后在 onCreateView 中,我像这样初始化 viewModel -

And then inside onCreateView I initlaize the viewModel like this -

viewModel = ViewModelProvider(this, Factory(requireActivity().application)).get(
   MainViewModel::class.java)

如果我想与 2 个片段共享一个视图模型,我如何对 navGraph viewModel 范围执行相同的操作?

How can I do the same with navGraph viewModel scope, If I want to share one view model with 2 fragments?

目前我有这种方法:

private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)

它的工作,但是

A.我从未见过 viewModel 直接在全局变量中被嵌入

A. I never saw viewModel intilazied right in the global variable

B.我无法使用这种方法在工厂内部传递变量

B. I can't pass variables inside factory with this approach

推荐答案

private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)

它的工作,但是

A.我从来没有看到 viewModel 在全局变量中被初始化

A. I never saw viewModel initialized right in the global variable

B.我无法使用这种方法在工厂内部传递变量

B. I can't pass variables inside factory with this approach

A.) 在这种情况下,ViewModel 在第一次访问时被初始化,所以如果你只是在 onCreateonViewCreated 中输入 homeViewModel 那么它会以正确的范围创建.

A.) ViewModel in this case is initialized on first access, so if you just type homeViewModel in onCreate or onViewCreated then it will be created with the right scope.

B.) 确实如此,您绝对可以使用带有 navGraphViewModels 的自定义工厂,但是您真正想要的(可能)是将您的任何 Fragment 参数隐式传递给 ViewModel(注意通过使用 SavedStateHandle,您的两个片段都必须在其参数中具有正确的键才能安全地工作).

B.) there's truth to this, you can definitely use a custom factory with navGraphViewModels, but what you really want (probably) is to implicitly pass any of your Fragment arguments to the ViewModel (note that both of your fragments must have the right keys in their arguments for this to work safely) by using a SavedStateHandle.

要获得SavedStateHandle,您需要使用AbstractSavedStateViewModelFactory.要创建一个,您必须在 onViewCreated 中创建您的 ViewModel(onCreate 不适用于导航图),这是使用 ViewModelLazy.

To obtain a SavedStateHandle, you need to use an AbstractSavedStateViewModelFactory. To create one, you must create your ViewModel inside onViewCreated (onCreate won't work with navgraphs), which is easiest to do by using a ViewModelLazy.

要创建viewModelLazy,您可以使用createViewModelLazy(罐头上写的).这可以定义一种方法来传递 ViewModelStoreOwner(即 NavBackStackEntry)和 SavedStateRegistryOwner(它也是 >NavBackStackEntry).

To create a viewModelLazy, you can use createViewModelLazy (what it says on the tin). This can define a way for you to pass in the ViewModelStoreOwner (which is the NavBackStackEntry), and the SavedStateRegistryOwner (which is ALSO the NavBackStackEntry).

所以你可以把它放在你的代码中,它应该可以工作.

So you can put this in your code and it should work.

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 ->
        ...
    }
}

这篇关于如何使用 navGraph 范围初始化 viewModel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆