如何使用 Hilt 为 viewpager 中的片段创建不同的视图模型实例(范围为父片段)

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

我有一个片段

HomeFragment
,它是应用程序导航图的一部分。在
FragmentHome
中,我有一个 viewpager,其中包含 3 个同一类
ChildFragment
的片段实例。每个
ChildFragment
都需要一个
ChildViewModel
的实例,定义如下

@HiltViewModel
class ChildViewModel @Inject constructor(
    private val repo: Repository
) : ViewModel() {

}

如何创建视图模型

ChildViewModel
的不同实例,其范围仅限于
HomeFragment
导航目的地的生命周期?

我尝试了以下行。但这在所有 3 个

ChildFragments
中共享相同的视图模型实例,这是不希望的。

private val vm by hiltNavGraphViewModels(R.id.homeFragment)

据我了解,ViewModelStore 使用一个 Key,它是视图模型的规范名称来保存它。有没有办法可以为每个片段使用自定义键,以便每个

ChildFragment
都有自己的实例?

我也尝试过

private lateinit var vm: ChildViewModel

...

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    super.onCreateView(inflater, container, savedInstanceState)
    parentFragment?.let {
        vm = ViewModelProvider(it).get(args.sectionId, CihldViewModel::class.java)
    }
}

但这引发了一些异常,因为它无法创建我的 ViewModel 实例。我对使用 Viewmodel Factory 方法有点不熟悉,因为 Hitl 为我们处理了它。

我想将此视图模型范围限制到

HomeFragment
导航目标的原因是为了保存状态,这样当用户重新登陆
HomeFragment

时,我可以为每个子片段重用相同的视图模型实例
android android-fragments viewmodel android-viewmodel dagger-hilt
1个回答
0
投票

所以,经过大量的挖掘和阅读,我终于弄清楚了,好吧。最初我希望使用 CreationExtras 或 Hilt AssistedInject 来实现这一目标。但不幸的是,这些都不起作用。

原因是,为了使自定义键起作用,我们必须将该自定义键传递给

ViewModelProvider.get()
并且以下委托都不会向其传递字符串键。因此,将创建一个 ViewModel 实例并使用默认键保存。

by viewModels()
by activityViewModels()
by navGraphViewModels()
by hiltNavGraphViewModels()

令人惊讶的是,这可以通过

hiltViewModel(viewModelStoreOwner: ViewModelStoreOwner, key: String?)
在 Compose 中实现,但同样不适用于 Fragment。

无论如何,这是对我有用的解决方案。如果您深入研究

by hiltNavGraphViewModels()
,您会发现它只是创建一个具有默认参数值的 ViewModelProvider,但
HiltViewModelFactory
除外,它负责将依赖项注入到我们的
HiltViewModel
带注释的 ViewModel 中。因此,我只需创建一个具有相同默认值的 ViewModelProvider 并根据需要传递我的自定义密钥。

private lateinit var vm: ChildViewModel

...

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    super.onCreateView(inflater, container, savedInstanceState)

    parentFragment?.let { it ->
        val backStackEntry = it.findNavController().getBackStackEntry(R.id.homeFragment)

        vm = ViewModelProvider(
            it,
            HiltViewModelFactory(
                requireActivity(),
                backStackEntry.defaultViewModelProviderFactory
            )
        )[args.sectionId, ChildViewModel::class.java]
    }
}

参考资料:

© www.soinside.com 2019 - 2024. All rights reserved.