Уведомлять наблюдателя, когда элемент добавлен в список 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

Больше будет добавлено, как позволяет время. Не стесняйтесь просить конкретные.

И, пожалуйста, сообщите о любых проблемах.