Уведомлять наблюдателя, когда элемент добавлен в список LiveData
Мне нужно получить событие Observer, когда элемент добавлен в список LiveData. Но, насколько я понимаю, событие получает только тогда, когда я заменяю старый список новым. Например, когда я делаю следующее:
list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))
Наблюдатель получает событие. Но когда я просто добавляю элемент к значению, Observer молчит. Не могли бы вы дать совет, как я могу реализовать то, что мне нужно?
Ответы
Ответ 1
Внутренне, LiveData отслеживает каждое изменение как номер версии (простой счетчик хранится как int). Вызов setValue() увеличивает эту версию и обновляет всех наблюдателей новыми данными (только если номер версии наблюдателя меньше, чем версия LiveData).
Похоже, что единственный способ запустить этот процесс - вызвать setValue() или postValue(). Побочным эффектом является то, что если базовая структура данных LiveData изменилась (например, добавление элемента в коллекцию), ничего не случится, чтобы сообщить об этом наблюдателям.
Таким образом, вам нужно будет вызвать setValue() после добавления элемента в список. Я предоставил два способа подойти к этому ниже.
Опция 1
Храните список за пределами LiveData и обновляйте его ссылками каждый раз, когда содержимое списка изменяется.
private val mIssuePosts = ArrayList<IssuePost>()
private val mIssuePostLiveData = MutableLiveData<List<IssuePost>>()
fun addIssuePost(issuePost: IssuePost) {
mIssuePosts.add(issuePost)
mIssuePostLiveData.value = mIssuePosts
}
Вариант 2
Отслеживайте список через LiveData и обновляйте LiveData своим собственным значением при каждом изменении содержимого списка.
private val mIssuePostLiveData = MutableLiveData<List<IssuePost>>()
init {
mIssuePostLiveData.value = ArrayList()
}
fun addIssuePost(issuePost: IssuePost) {
mIssuePostLiveData.value?.add(issuePost)
mIssuePostLiveData.value = mIssuePostLiveData.value
}
Любое из этих решений должно помочь вам от необходимости создавать новый список каждый раз, когда вы изменяете текущий список, просто чтобы уведомить наблюдателей.
ОБНОВИТЬ:
Я уже некоторое время использую подобные приемы, поскольку Gnzlt упомянул в своем ответе использование функции расширения Kotlin для присвоения себе LiveData для упрощения кода. Это по сути вариант 2 автоматизированный :) Я бы рекомендовал это сделать.
Ответ 2
Я использую функцию Extension Function
Kotlin, чтобы упростить ее:
fun <T> MutableLiveData<T>.notifyObserver() {
this.value = this.value
}
Затем используйте его в любом MutableLiveData
следующим образом:
fun addIssuePost(issuePost: IssuePost) {
mIssuePostLiveData.value?.add(issuePost)
mIssuePostLiveData.notifyObserver()
}
Ответ 3
Как насчет этого?
public class ListLiveData<T> extends LiveData<List<T>> {
public void addAll(List<T> items) {
if (getValue() != null && items != null) {
getValue().addAll(items);
setValue(getValue());
}
}
public void clear() {
if (getValue() != null) {
getValue().clear();
setValue(getValue());
}
}
@Override public void setValue(List<T> value) {
super.setValue(value);
}
@Nullable @Override public List<T> getValue() {
return super.getValue();
}
}
// add changed listener
mMessageList.observe(mActivity, new Observer() {
@Override public void onChanged(@Nullable Object o) {
notifyDataSetChanged();
}
});
Ответ 4
Вдохновленный @user3682351 вот мое решение. Кажется, что мы не можем обновлять свойства значения LiveData
отдельности, поэтому значение должно обновляться при каждой операции. По сути, это небольшая оболочка для живых данных с удобными методами для изменения свойств HashMap
import androidx.lifecycle.LiveData
/**
* Hash Map Live Data
*
* Some convenience methods around live data for HashMaps. Putting a value on this will also update the entire live data
* as well
*/
class HashMapLiveData<K, V> : LiveData<HashMap<K, V>>() {
/**
* Put a new value into this HashMap and update the value of this live data
* @param k the key
* @param v the value
*/
fun put(k: K, v: V) {
val oldData = value
value = if (oldData == null) {
hashMapOf(k to v)
} else {
oldData.put(k, v)
oldData
}
}
/**
* Add the contents to the HashMap if there is one existing, otherwise set the value to this HashMap and update the
* value of this live data
* @param newData the HashMap of values to add
*/
fun putAll(newData: HashMap<K, V>) {
val oldData = value
value = if (oldData != null) {
oldData.putAll(newData)
oldData
} else {
newData
}
}
/**
* Remove a key value pair from this HashMap and update the value of this live data
* @param key the key to remove
*/
fun remove(key: K) {
val oldData = value
if (oldData != null) {
oldData.remove(key)
value = oldData
}
}
/**
* Clear all data from the backing HashMap and update the value of this live data
*/
fun clear() {
val oldData = value
if (oldData != null) {
oldData.clear()
value = oldData
}
}
var value: HashMap<K, V>?
set(value) = super.setValue(value)
get() = super.getValue()
}
Ответ 5
Я столкнулся с той же проблемой и решил, что добавление "value = value" или вызов другого метода везде слишком сложен и очень подвержен ошибкам.
Итак, я создал свои собственные классы, которые обертывают коллекции. Наблюдать теперь будет наблюдать содержимое коллекции вместо самой коллекции.
Код доступен на GitHub по адресу: ObservableCollections
Это ПЕРВАЯ версия, поэтому прошу прощения за любые проблемы. Он пока недоступен для включения в Gradle, поэтому вам придется скачать его и добавить в качестве библиотечного модуля в ваше приложение.
В настоящее время он включает в себя следующие коллекции:
ArrayDeque
ArrayList
LinkedList
Queue
Stack
Vector
Больше будет добавлено, как позволяет время. Не стесняйтесь просить конкретные.
И, пожалуйста, сообщите о любых проблемах.