如何为父片段和子片段提供相同的 ViewModel 实例

How to give same instance of ViewModel to both the Parent and Child fragment

有两个片段:ParentFragment 和 ChildFragment。 ChildFragment 已添加到 ParentFragment 的视图中。

现在对 Android 使用 Dagger2ParentFragmentModule 方法:

@Provides
fun provideViewModel(fragment: ParentFragment, myViewModelFactory: MyViewModelFactory): MyViewModel {
    return ViewModelProviders.of(fragment, myViewModelFactory).get(MyViewModelImpl::class.java)
}

其中 MyViewModelFactory、MyViewModel、MyViewModelImpl 是在应用程序中创建的简单 ViewModel 逻辑。

并且 ChildFragmentModule 具有以下方法:

@Provides
fun provideViewModel(fragment: ChildFragment, myViewModelFactory: MyViewModelFactory): MyViewModel {
    return ViewModelProviders.of(fragment, myViewModelFactory).get(MyViewModelImpl::class.java)
}

这显然是在创建 ViewModel 的两个独立实例,因为它们接收两个不同的片段实例。

我们如何使它return成为同一个实例,以便数据可以在父片段和子片段之间共享?

我尝试在 ChildFragmentModule 中传递 ParentFragment 而不是 ChildFragment,但这会导致 Dagger 依赖注入错误。

Activity 范围创建您的 ViewModel。然后 Activity 中的所有 Fragment 将获得相同的 ViewModel 实例。

查看官方 ViewModelProviders 参考资料。您可以使用 ActivityFragment 范围创建 ViewModel

ViewModelProvider of (FragmentActivity activity)

Creates a ViewModelProvider, which retains ViewModels while a scope of given Activity is alive. More detailed explanation is in ViewModel.

ViewModelProvider of (Fragment fragment)

Creates a ViewModelProvider, which retains ViewModels while a scope of given fragment is alive. More detailed explanation is in ViewModel.

用于创建 ViewModel

的示例代码

来自 Activity:

 movieListViewModel = ViewModelProviders.of(this).get(MovieListViewModel.class);

来自片段:

 movieListViewModel = ViewModelProviders.of(getActivity()).get(MovieListViewModel.class);

使用Fragment-ktx我们可以像In **ParentFragment**

那样做
 private val viewModel: DemoViewModel by viewModels()

In ChildFragment

 private val viewModel: DemoViewModel by viewModels(
    ownerProducer = { requireParentFragment() }
)

这样做我们可以在 ParentFragmentChildFragment[ 中获得相同的 ViewModel 实例

app中添加依赖 -> build.gralde

implementation 'androidx.fragment:fragment-ktx:1.1.0

在您的 应用中实施 fragment-ktx -> build.gradle:

implementation 'androidx.fragment:fragment-ktx:1.2.5

如果您使用的是导航组件 (https://developer.android.com/guide/navigation)

在parentFragment中,可以这样获取viewModel:

private val viewModel by viewModels<DemoViewModel>()

然后当您需要 childFragment 中的 viewModel 时,您可以通过以下方式获取它:

private val viewModel by viewModels<DemoViewModel>({requireGrandParentFragment()})

requireGrandParentFragment() 是 Fragment 的自定义扩展:

fun Fragment.requireGrandParentFragment() = this.requireParentFragment().requireParentFragment()

如果您没有使用导航组件

您可以这样访问它:

private val viewModel by viewModels<DemoViewModel>({requireParentFragment()})

我在所有解决方案中都遇到了同样的问题,但在我的场景中没有工作想出另一个解决方案,使用 requireParentFragment() return NavHostFragment in child fragment 通过使用

解决它
private val viewModel: childViewModel by viewModels(
            ownerProducer = { requireParentFragment().childFragmentManager.primaryNavigationFragment!! }
    )

在Parent Fragmet中使用这个

private val viewModel: MyOrdersVM by viewModels()

ViewModelProvider 构造函数有 owner 参数

ViewModelProvider(
    ViewModelStoreOwner owner,
    ViewModelProvider.Factory factory
)

参考official docs.

此参数负责 ViewModel 实例保持不变的范围。您需要在 ViewModelStoreOwner 上传递相同的引用才能拥有 ViewModel

的相同实例

如果在两个相互依赖的片段之间共享 ViewModel 作为父子,您需要像这样获取 ViewModel 实例:

在父片段:

viewModel = ViewModelProvider(
  this, 
  viewModelFactory
).get(ProfileViewModel::class.java) 

在子片段:

viewModel = ViewModelProvider(
  requireParentFragment(), 
  viewModelFactory
).get(ProfileViewModel::class.java) 

This resource 可能有助于了解更多信息。