Как передать один и тот же экземпляр ViewModel как родительскому, так и дочернему фрагменту

Есть два фрагмента: ParentFragment и ChildFragment. ChildFragment был добавлен в список ParentFragment.

Теперь с помощью Dagger2 для Android есть ParentFragmentModule с методом:

@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, поскольку они получают два разных экземпляра фрагмента.

Как сделать так, чтобы он возвращал один и тот же экземпляр, чтобы данные могли совместно использоваться как родительским, так и дочерним фрагментами?

Я попытался передать ParentFragment вместо ChildFragment в ChildFragmentModule, но это привело к ошибке внедрения зависимости Dagger.

13
1
11 107
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Создайте свой ViewModel с помощью прицела Activity. Тогда все Fragment в этом Activity получат один и тот же экземпляр ViewModel.

Проверьте официальную ссылку ViewModelProviders. Вы можете создать ViewModel как с прицелом Activity, так и с Fragment.

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

Из активности:

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

Из фрагмента:

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

Спасибо. Это работает гладко! Просто для ознакомления, области видимости родительского и дочернего фрагментов работают одновременно. Как добиться того же без использования Activity?

mhtmalpani 26.05.2018 16:46

Как поделиться movieListViewModel с другим модулем? Это возможно

Fazal Hussain 28.08.2018 14:35

А как насчет подхода единой деятельности? Нецелесообразно связывать все модели общего представления с одним действием.

A. Kazarovets 30.10.2018 13:58

просто модификация, чтобы избежать исключения NullPointerException movieListViewModel = ViewModelProviders.of (requireActivity ()). get (MovieListViewMo‌ del.class);

Zain 16.01.2019 17:46

Чтобы добавить к этому, можно ли получить модель представления для отдельного действия для отправки данных в дочерний фрагмент MainActivity?

Victor Okech 26.12.2020 11:32

Используя Фрагмент-ktx, мы можем сделать как In **ParentFragment**

 private val viewModel: DemoViewModel by viewModels()

А также

In ChildFragment

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

Делая это, мы можем получить тот же экземпляр ViewModel в Родительский фрагмент и Детский фрагмент

добавить зависимости в приложение -> build.gralde

implementation 'androidx.fragment:fragment-ktx:1.1.0

Реализуйте фрагмент-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>()

И затем, когда вам понадобится этот viewModel в childFragment, вы можете получить его следующим образом:

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

requireGrandParentFragment () - это настраиваемое расширение фрагмента:

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

Если вы не используете компонент навигации

вы можете получить к нему доступ так:

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

У меня такая же проблема, как и все решение, но не работающая в моем сценарии, я предлагаю другое решение с использованием requireParentFragment () return NavHostFragment в дочернем фрагменте, решите его, используя

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

в Parent Fragmet используйте это

private val viewModel: MyOrdersVM by viewModels()

Другие вопросы по теме