ViewModel для фрагмента вместо доступа к деятельности ViewModel?
Проблема довольно проста. Вопрос в том, что касается использования ViewModels, LiveData и других связанных с Lifecycle арочных подходов.
У меня есть активность с NavDrawer, который переключает фрагменты внутри.
А также у меня есть случай, когда на экране одновременно присутствуют два фрагмента - это будет основной болью. Один фрагмент имеет ViewPager с вложенным Fragments
(не спрашивай почему). Другой фрагмент просто получает информацию от первого, когда пользователь выполняет некоторые действия. Это достигается просто путем совместного использования активности viewmodel. Но само приложение имеет много бизнес-логики, и по мере продвижения модель представления становится все больше и больше.
То, что я хочу спросить - не квитанция или правила, как это исправить, или, может быть, как преодолеть это, исправив всю структуру проекта. Я хочу попросить совета, как я могу применить подход 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
здесь, чтобы помочь?
PS - Ну, создание UML или других диаграмм - не моя сильная сторона. Простите за это.
PPS - мне известны образцы Google.
2 ответа
То, что я хотел бы предложить, вы можете справиться с двумя 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);
Во-первых, нет никакого вреда в наличии нескольких ViewModel
это для одного взгляда.
Я хотел бы подумать о том, что моя модель представления похожа на то, какие данные получают и обрабатывают, и группировать их таким образом, который кажется естественным.
Для вашего случая, если фрагменты и логика действия очень похожи, я думаю, что вы можете пойти с одним ViewModel
, но я бы этого избежать.
Что бы я сделал, это сломать деятельность ViewModel
на более мелкие части и повторно использовать правильное ViewModel
в моем Fragments
, чтобы у меня не было бога ViewModel или примерно одинакового кода в разных ViewModel.
Это обновленная версия ответа Джила Ванкхеде. А также реализация Kotlin того же самого.
Поскольку ViewModelProviders устарел, мы должны использовать ViewModelProvider.
Вот как вы это делаете в Activity:
ViewModelProvider(this).get(MyActivityViewModel::class.java)
Вот как вы это делаете во фрагменте:
ViewModelProvider(requireActivity()).get(MyActivityViewModel::class.java)
Чтобы решить проблему FirstFragment
разделяя свою модель представления с дочерними фрагментами, вы можете использовать этот код для доступа к FirstFragmentViewModel
из любого дочернего фрагмента:
// in ChildFragment1
val firstFragmentViewModel: FirstFragmentViewModel by viewModels(
{ requireParentFragment() }
)