Внедрить ViewModel активности в ViewModel фрагмента

Я настроил проект, очень похожий на GithubBrowserSample. Итак, установка кинжала такая же.

Учтите, что есть ActivityViewModel и FragmentViewModel, конструктор которых имеет ненулевой аргумент, поэтому они получаются из ViewModelProviders через пользовательский ViewModelProvider.Factory.

Я хочу дать указание кинжалу ввести уже созданный экземпляр ActivityViewModel в следующем коде:


    class FragmentViewModel @Inject constructor(
        private val activityViewModel: ActivityViewModel
        private val foo: Foo
    ) : ViewModel() {
        ...
    }

С текущим объявлением Dagger создаст новый экземпляр ActivityViewModel независимо от того, что он уже существует.

Это происходит потому, что существует аннотированный конструктор @Inject для ActivityViewModel.

Таким образом, dagger может предположить, что это правильный способ предоставления экземпляра ActivityViewModel для FragmentViewModel.

Я знаю, как делать вещи для обычного Даггера, но я не знаю, как это сделать для Даггера-Андроида, и это вопросы конкретно для настройки Даггера-Андроида.

В качестве грязного решения я сейчас вручную устанавливаю этот экземпляр:


    class MyFragment : Fragment {
      ...
      override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(FragmentViewModel::class.java)
        viewModel.activityViewModel = ViewModelProviders.of(activity!!, viewModelFactory).get(ActivityViewModel::class.java)
      }
      ...
    }

Как правильно внедрить родительский ViewModel в дочерний ViewModel?


person azizbekian    schedule 11.08.2018    source источник
comment
Зачем вам вообще нужна одна модель представления в другой?   -  person Deividas Strioga    schedule 11.08.2018
comment
@DeividasStrioga, в чем проблема? Предположим, я хочу делегировать какую-то работу родительскому ViewModel, потому что это не работа дочернего ViewModel, то есть установление связи между фрагментами.   -  person azizbekian    schedule 11.08.2018
comment
Согласно их назначению, они должны помогать заполнять представление и реагировать на события пользовательского интерфейса. Фрагменты/действия должны выдавать команды и обмениваться данными между ними. Мне кажется, что это лучшее архитектурное решение.   -  person Deividas Strioga    schedule 11.08.2018
comment
не должно ли общение осуществляться через общую модель или модель для модели, о которой знает каждая модель представления, а не модели представления, знающие о другой модели представления? Я пытаюсь понять, как любое делегирование должно быть необходимо, конечно, информация должна быть представлена ​​​​правильной модели представления из модели напрямую, а не через другую модель представления.   -  person Mark Keen    schedule 11.08.2018
comment
@DeividasStrioga, предположим, что у вас есть ProgressBar внутри вашей активности, которая в настоящее время скрыта. Теперь внутри вашего фрагмента вы выполняете действие, которое должно инициировать отображение ProgressBar. Как вы собираетесь наладить это общение?   -  person azizbekian    schedule 11.08.2018
comment
@azizbekian Прежде всего, я бы использовал привязку данных, если при нажатии кнопки какого-либо фрагмента отображается индикатор выполнения, легко настроить прослушиватель для воздействия на метод действия. Если бы я не использовал привязку данных, я бы сделал это из фрагмента, напрямую информируя его об активности.   -  person Deividas Strioga    schedule 11.08.2018
comment
@DeividasStrioga, сэр, какая разница, используете ли вы DataBinding или обычный findViewById()? I would do it from fragment directly informing it's activity Почему фрагмент должен знать что-то об иерархии представлений активности?   -  person azizbekian    schedule 11.08.2018
comment
@azizbekian Просто с привязкой данных проще. Хорошо, что фрагмент, знающий о своей родительской деятельности, более уместен, чем модель представления фрагмента, знающая о модели представления другого представления (действия). Модель представления не должна ничего знать о своем собственном представлении, не говоря уже о помощнике другого представления. У вас есть несколько фрагментов в активности ono одновременно?   -  person Deividas Strioga    schedule 11.08.2018
comment
МОЖЕТ быть, вы могли бы установить слушателя из активности в модель представления фрагмента после ее создания.   -  person Deividas Strioga    schedule 11.08.2018
comment
It is just easier with databinding Спасибо, я уже использую привязку данных, я просто имел в виду, что это не связано с фактическим вопросом. В моем случае ViewModel ничего не знает ни о своем представлении, ни о помощнике другого представления. Мой FragmentViewModel делегирует задание ActivityViewModel, который, в свою очередь, отправляет событие в активность через LiveData.   -  person azizbekian    schedule 11.08.2018
comment
MAybe you could set a listener from activity to fragment's viewmodel after it's creation Это именно то решение, которое я отметил в вопросе как грязное. Спасибо за мысли.   -  person azizbekian    schedule 11.08.2018
comment
@azizbekian помощником другого представления, я имею в виду, что это модель представления. It's exactly the solution that I've marked in the question as "dirty". Нет, я предлагаю, чтобы ваша активность реализовывала какой-то интерфейс, скажем, ProgressBarListener. Во фрагменте onCreate устанавливает этот слушатель из активности в эту модель представления (если это событие не происходит из действия пользовательского интерфейса). По крайней мере, я бы так сделал. Надеюсь, это поможет.   -  person Deividas Strioga    schedule 11.08.2018
comment
@azizbekian то, чего вы пытаетесь достичь, можно сделать с правильным объемом модуля. Можете ли вы поделиться своим фрагментом?   -  person Ayokunle Paul    schedule 01.10.2018


Ответы (1)


Это не ответ на вопрос, скорее это подход, который я придумал и использую в настоящее время.

Объявив следующие функции расширения:

inline fun <reified T : ViewModel> Fragment.getViewModel(
    factory: ViewModelProvider.Factory = ViewModelProvider.NewInstanceFactory()
) = ViewModelProviders.of(this, factory).get(T::class.java)

inline fun <reified T : ViewModel> Fragment.getParentViewModel(
    factory: ViewModelProvider.Factory = ViewModelProvider.NewInstanceFactory()
) = ViewModelProviders.of(activity!!, factory).get(T::class.java)

Затем в классе фрагментов мы можем объявить следующее:

private val parentViewModel by lazy { getParentViewModel<ParentViewModel>(viewModelFactory) }
private val childViewModel by lazy {
  val field = getViewModel<ChildViewModel>(viewModelFactory)
  field.parentViewModel = parentViewModel
  field
}
person azizbekian    schedule 27.12.2018