如何使用 navGraph 范围初始化 viewModel

Noa*_*oam 5 android mvvm kotlin android-viewmodel android-jetpack

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

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

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

private lateinit var viewModel: MainViewModel
Run Code Online (Sandbox Code Playgroud)

然后在里面onCreateView我像这样初始化 viewModel -

viewModel = ViewModelProvider(this, Factory(requireActivity().application)).get(
   MainViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

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

目前我有这种方法:

private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)
Run Code Online (Sandbox Code Playgroud)

这是工作,但是

A.我从来没有在全局变量中看到过 viewModel

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

Epi*_*rce 6

private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)
Run Code Online (Sandbox Code Playgroud)

And It's work, but

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

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

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.) 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。要创建一个,您必须在内部创建您的 ViewModel onViewCreatedonCreate不适用于导航图),使用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
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

现在你可以做

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 ->
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)