ViewModel для фрагмента вместо доступа к Activity ViewModel?

Проблема довольно проста. Вопрос в контексте использования ViewModels, LiveData и других связанных жизненного цикла Подходит знающая арка.
У меня есть Activity с NavDrawer, который переключает фрагменты внутри.
А еще у меня есть случай, когда на экране одновременно присутствуют два фрагмента - это будет основная боль. Один фрагмент имеет ViewPager с вложенным Fragments (не спрашивайте почему). Другой фрагмент просто получает информацию от первого, когда пользователь выполняет какие-то действия. Это достигается просто путем совместного использования модели просмотра активности. Но в самом приложении много бизнес-логики, и по мере продвижения модель просмотра становится все больше и больше.
Я хочу спросить - не квитанцию ​​или правила, как это исправить, или, может быть, как это преодолеть, исправив всю структуру проекта. Я хочу попросить совета, как применить подход MVVM в стиле android.arch.lifecycle к моему варианту использования.
Я не видел чего-то более сложного, чем просто совместное использование Activity ViewModel между фрагментами. Но обычное дело, это не лекарство. введите описание изображения здесь

То, что вы здесь видите - на самом деле беспорядок. Дело в том, что все разделяют ActivityViewModel. Соединения (агрегация) от FirstFragment означают, что ViewPager внутри FirstFragment инициирует ChildFragments, и они также работают с тем же ActivityViewModel (убейте меня). В результате все работают с одной общей ViewModel.
Мое предложение - добавить ViewModel для каждого слоя. Так что Activity / Fragments / ChildFragments имеют свои собственные ViewModels. Но что здесь появляется - как нам тогда общаться?
Возможные решения:

  • Наличие двух ViewModels на один компонент. Одна ViewModel будет обрабатывать / делегировать бизнес-логику, а другая - обеспечивать связь. Две модели просмотра на компонент - не очень хорошо, да?
  • Старый интерфейс (пожалуйста, нет!)
  • Другие обходные пути - например, прослушиватели изменений DB / SharedPrefs / Realm и шины событий (Я слишком стар для этого :().

  • Ваше решение здесь!

Я скажу, что все вышеперечисленное нарушает многие принципы дизайна, так что мне делать? Как мне выйти из этого беспорядка? Кто-нибудь Uncle Bob или другой superhero здесь, чтобы помочь?

P.S. - Что ж, создание UML или других диаграмм не моя сильная сторона. Простите за это.
PPS - мне известно о google < href = "https://github.com/googlesamples/android-architecture" rel = "noreferrer"> образцы.


person Yurii Tsap    schedule 17.09.2017    source источник
comment
Что плохого в наличии нескольких ViewModels на компонент? Не зря они привязаны к имени класса.   -  person ianhanniballake    schedule 18.09.2017
comment
@ianhanniballake Хм, мои мысли по этому поводу противоречивы. Всегда пытался привязать только одну виртуальную машину к одному компоненту. Допустим, виртуальная машина на компонент. github.com/googlesamples/android-architecture-components/issues/ - не то же самое, но модель просмотра на экране.   -  person Yurii Tsap    schedule 18.09.2017
comment
когда у меня было много моделей просмотра, все коммуникации, которые я осуществлял через потоки и изменение переменных в общих менеджерах / моделях, например. первый фрагмент изменяет ввод на 6 и устанавливает его в диспетчере (модели) и диспетчере с использованием наблюдаемого шаблона (в вашем случае это могут быть изменяемые данные) уведомить наблюдателей (другие фрагменты, наблюдающие за переменной данных в реальном времени), что значение изменилось   -  person V-master    schedule 05.10.2017
comment
@ V-master Конечно, но в таком случае мне нужна ссылка на модель / менеджера. Поэтому я буду ссылаться на две модели просмотра в моем фрагменте.   -  person Yurii Tsap    schedule 05.10.2017
comment
Каков был ваш вывод?   -  person Gilberto Ibarra    schedule 15.11.2017


Ответы (4)


Я бы посоветовал вам обработать два ViewModel для всего вашего варианта использования.

Сделайте один ViewModel

Скажем, MyActivityViewModel, чтобы обрабатывать всю логику, относящуюся к уровню активности. Итак, если какая-либо логика фрагмента напрямую связана с вашей деятельностью, поделитесь своим ViewModel, как показано ниже:

ViewModelProviders.of(getActivity()).get(MyActivityViewModel.class); // Like this in fragment.

&

ViewModelProviders.of(this).get(MyActivityViewModel.class); // Like this in activity.

Это будет общее ViewModel между вашим действием и фрагментом.


Другой ViewModel пойдет на FirstFragment в вашем случае, если вам нужно разделить логику между вашими ChildFragment:

Здесь вы можете поделиться ViewModel, скажем, FragmentViewModel, как показано ниже:

ViewModelProviders.of(this).get(FragmentViewModel.class); // Like this in FirstFragment which is having view pager.

&

ViewModelProviders.of(getParentFragment()).get(FragmentViewModel.class); // Like this in View pager fragments, getParentFragment() is First fragment in our case.

Хотя мы все еще можем использовать наш уровень активности MyActivityViewModel в наших дочерних фрагментах из FirstFragment, например:

ViewModelProviders.of(getActivity()).get(MyActivityViewModel.class);
person Jeel Vankhede    schedule 10.10.2018

Во-первых, нет ничего плохого в наличии нескольких ViewModel для одного просмотра.

Я бы подумал о своей ViewModel, как о том, какие данные получает и обрабатывает, и сгруппировал бы их таким образом, который кажется естественным.

В вашем случае, если фрагменты и логика активности очень похожи, я думаю, вы можете использовать один ViewModel, но я бы этого избегал.

Что бы я сделал, так это разбил ViewModel действия на более мелкие части и повторно использовал правильные ViewModel в моем Fragments, чтобы у меня не было ни модели представления Бога, ни примерно того же кода в другой модели представления .

person Róbert Nagy    schedule 24.01.2018

Это обновленная версия ответа Джила Ванкхеде. А также реализация того же самого в Котлине.

Поскольку ViewModelProviders устарел, мы должны использовать ViewModelProvider.

Вот как вы это делаете в Activity:

ViewModelProvider(this).get(MyActivityViewModel::class.java)

Вот как вы это делаете во фрагменте:

ViewModelProvider(requireActivity()).get(MyActivityViewModel::class.java)
person Dashesh    schedule 21.08.2020
comment
Это ответ, который мы все ищем: простой, работает. [аплодисменты] - person SMBiggs; 06.06.2021

Чтобы решить проблему FirstFragment совместного использования своей модели представления с дочерними фрагментами, вы можете использовать этот код для доступа к FirstFragmentViewModel из любого из дочерних фрагментов:

    // in ChildFragment1
    val firstFragmentViewModel: FirstFragmentViewModel by viewModels(
        { requireParentFragment() }
    )

person Gilboot    schedule 16.05.2020