Посмотреть модели для предметов RecyclerView - PullRequest
5 голосов
/ 22 апреля 2020

У моей активности есть Google ViewModel, который выбирает некоторые элементы модели. Эти элементы затем преобразуются в элементы адаптера RecyclerView. Существует также много типов элементов адаптера, поддерживаемых одним RecyclerView.

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

В настоящее время, когда у меня есть некоторые асинхронные логики c (которые должны быть остановлены в onCleared ()), которые связаны только с некоторым элементом адаптера, я должен каким-то образом перенаправить обратные вызовы через модель основного вида так что все должным образом не зарегистрировано.

Я подумывал об использовании ViewModelProvider::get(key, modelClass), но мои предметы со временем меняются, и я не могу найти хороший способ "очистить" старые предметы.

Как Вы обрабатываете эти случаи в своих проектах?

Редактировать: Чтобы добавить больше информации о моей проблеме, возможно, другими словами: я хочу, чтобы моя «маленькая» модель ViewMlel проживала столько, сколько элемент модели, который она представляет. Это означает, что:

  • Я должен получить обратный вызов onCleared () в том же сценарии ios, в котором родительские элементы этих элементов получают
  • Я должен получить обратный вызов onCleared (), когда элемент больше не

Редактировать: попробуйте сравнить его с ViewPager с фрагментами в качестве элементов. Каждый отдельный элемент модели представлен в виде фрагмента с его ViewModel. Я хотел бы добиться чего-то похожего, но для RecyclerView.

Ответы [ 2 ]

4 голосов
/ 27 апреля 2020

ViewModels не предназначены для использования с элементами RecyclerView

Почему?

ViewModel - это AA C (Android Компонент архитектуры ) , единственная цель которого - пережить изменения конфигурации жизненного цикла Android Activity / Fragment , чтобы в таком случае данные можно было сохранить через ViewModel.

Вот почему это не должно не может быть использовано в RecyclerView (ViewHolder) Элементы, поскольку само представление элемента будет частью Activity / Fragment, и оно (RecyclerView / ViewHolder) не содержит никакого специального c API ViewModelStoreOwner (из которого ViewModel в основном получены для данного экземпляра Activity / Fragment) .

Simplisti c синтаксис для получения ViewModel:

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

& here this будет относиться к контексту действия / фрагмента.

Так что даже если вы в конечном итоге будете использовать ViewModel в RecyclerView Items, это даст вам тот же самый экземпляр из-за к контексту может быть активность / фрагмент одинаков на протяжении всей корзины rView, который не имеет смысла для меня. Таким образом, ViewModel бесполезен для RecyclerView или не сильно способствует этому делу.


TL; DR

Решение?

Вы можете напрямую передать LiveData объект, который вам нужно наблюдать из вашей деятельности / фрагмента ViewModel в вашем RecyclerView.Adapter классе. Вам также понадобится предоставить LifecycleOwner для адаптера, чтобы начать наблюдать эти данные в реальном времени.

Таким образом, ваш класс Adapter будет выглядеть примерно так:

class RecyclerViewAdapter(private val liveDataToObserve: LiveData<T>, private val lifecycleOwner: LifecycleOwner) : RecyclerView.Adapter<ViewHolder>() {

    init {
        liveDataToObserve.observe(lifecycleOwner) { t ->
            // Notify data set or something...
        }
    }

}

Если это это не так, и вы хотите, чтобы он был в классе ViewHolder, тогда вы можете передать объект LiveData во время метода onCreateViewHolder в свой экземпляр ViewHolder вместе с lifecycleOwner.

Бонус!

Если вы используете привязку данных к элементам RecyclerView, вы можете легко получить объект lifecyclerOwner из своего класса привязки. Все, что вам нужно сделать, это установить его во время onCreateViewHolder(), как показано ниже:

class RecyclerViewAdapter(private val liveDataToObserve: LiveData<T>, private val lifecycleOwner: LifecycleOwner) : RecyclerView.Adapter<ViewHolder>() {

    override fun onCreateViewHolder: ViewHolder {
        // Some piece of code for binding
        binding.lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
        // Another piece of code and return viewholder
    }

}

class ViewHolder(private val someLiveData: LiveData<T>, binding: ViewDataBinding): RecyclerView.ViewHolder(binding.root) {

    init {
        someLiveData.observe(requireNotNull(binding.lifecycleOwner)) { t->
            // set your UI by live data changes here
        }
    }
}

Так что да, вы можете использовать класс-оболочку для ваших ViewHolder экземпляров, чтобы предоставить вам LiveData из коробки, но Я бы не одобрял это, если класс-оболочка расширяет класс ViewModel.

Как только вы захотите имитировать onCleared() метод ViewModel, вы можете создать метод для вашего класса-оболочки, который будет вызываться при ViewHolder перерабатывается или отсоединяется от окна методом onViewRecycled() или onViewDetachedFromWindow() в зависимости от того, что лучше всего подходит для вашего случая.


Редактировать для комментария @Mariusz: беспокойство по поводу использования Activity / Fragment как LifecycleOwner является правильным. Но было бы немного непонимание, считая это как PO C.

Как только кто-то использует lifecycleOwner для наблюдения LiveData в данном RecyclerViewHolder элементе, это нормально, потому что LiveData является компонентом, поддерживающим жизненный цикл, и он обрабатывает подписку на жизненный цикл внутри, поэтому безопасен в использовании. Даже если вы можете явно удалить наблюдение, если хотите, используя метод onViewRecycled() или onViewDetachedFromWindow().

Об асинхронной c операции внутри ViewHolder:

  1. Если вы используете сопрограммы, то вы можете использовать lifecycleScope из lifecycleOwner для вызова вашей операции, а затем предоставить данные обратно конкретному наблюдению LiveData без явной обработки случая очистки (LifecycleScope позаботится об этом для вас) .

  2. Если вы не используете Coroutines, вы все равно можете сделать свой вызов asy c и предоставить данные обратно для наблюдения LiveData и не беспокоиться о том, чтобы очистить ваш Операция asyn c во время onViewRecycled() или onViewDetachedFromWindow() обратных вызовов. Здесь важно отметить LiveData, который учитывает жизненный цикл данной LifecycleOwner, а не текущую асинхронную c операцию.

0 голосов
/ 30 апреля 2020

Хотя это правда, что Android использует ViewModels в Android Компоненты архитектуры , это не значит, что они являются частью AA C. Фактически, ViewModels являются одним из компонентов шаблона архитектуры MVVM , который не связан только с Android. Таким образом, фактическая цель ViewModel - , а не , чтобы сохранить данные в течение Android жизненного цикла изменений. Тем не менее, из-за предоставления своих данных без ссылки View делает его идеальным для случая Android Speci c, в котором View может быть воссоздан без влияния на компонент, который хранит свое состояние ( ViewModel ). Тем не менее, у него есть и другие преимущества, такие как: Разделение проблем среди других.

Также важно отметить, что ваш случай не может быть на 100% по сравнению с ViewPager- Фрагменты , поскольку основное отличие состоит в том, что ViewHolders будет переработано между элементами. Даже если ViewPager Фрагменты будут уничтожены и созданы заново, они все равно будут представлять тот же Фрагмент с теми же данными. Именно поэтому они могут безопасно связывать данные, предоставленные их уже существующими ViewModel. Однако в случае ViewHolder, когда он воссоздается, он может представлять совершенно новый элемент, поэтому данные, которые его предполагаемый ViewModel может предоставить, могут быть неверными, ссылаясь на старый элемент.

То При этом можно легко сделать ViewHolder ViewModelStoreOwner:

class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), ViewModelStoreOwner {

    private var viewModelStore: ViewModelStore = ViewModelStore()

    override fun getViewModelStore(): ViewModelStore = viewModelStore
}

Это может быть полезно, если данные, предоставленные ViewModel, одинаковы независимо от ViewHolder. элемент (общее состояние между всеми элементами). Однако, если это не так, вам нужно будет сделать недействительным ViewModelStore, вызвав viewModelStore.clear() и создать новый экземпляр ViewModel, вероятно, в ViewHolder onViewRecycled. Вы потеряете преимущество сохранения состояния вне зависимости от представления * жизненного цикла , но иногда это может быть полезно, если следовать Разделение проблем .

Наконец, что касается возможности использования экземпляра LiveData для управления состоянием, независимо от того, предоставляется ли оно общим или указанным ViewHolder или указанным c ViewModel или оно передается через Adapter, вам понадобится LifecycleOwner для его наблюдения. Лучший подход к использованию текущего жизненного цикла Fragment или Activity состоит в том, чтобы просто использовать фактический жизненный цикл спецификаций c ViewHolder, поскольку они фактически создаются и уничтожаются, заставляя их реализовывать интерфейс LifecycleOwner. Я создал небольшую библиотеку , которая делает именно это.

...