如何使用navGraph范围初始化viewModel

问题描述 投票:2回答:1

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

我想为它们两个创建共享的navGraph viewModel范围,但是我不明白如何以及在何处可以在这些片段中初始化视图模型。

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

private lateinit var viewModel: MainViewModel

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

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

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

目前,我有这种方法:

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

并且可以,但是

A。我从未在全局变量中看到过viewModel正确的声音

B。我无法通过这种方法在工厂内部传递变量

android kotlin mvvm android-jetpack android-viewmodel
1个回答
0
投票
private val homeViewModel: HomeViewModel by navGraphViewModels(R.id.nested_navigation)

并且可以,但是

A。我从未在全局变量中看到过viewModel初始化]

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

A。]在这种情况下,ViewModel会在首次访问时初始化,因此,如果仅在homeViewModelonCreate中键入onViewCreated,则会在正确的范围内创建它。

B。)这是事实,您可以在navGraphViewModels中使用自定义工厂,但是您真正想要的(可能)是隐式地将任何Fragment参数传递给ViewModel(请注意,您的两个片段都必须可以使用SavedStateHandle在其参数中使用正确的键,以使其安全地工作)。

要获得SavedStateHandle,您需要使用AbstractSavedStateViewModelFactory。要创建一个视图,必须在onViewCreated中创建ViewModel(onCreate与导航图不兼容),这最简单的方法是使用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 ->
        ...
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.