将项目添加到LiveData列表时通知观察者

时间:2017-12-22 12:25:43

标签: android kotlin android-livedata

当项目添加到LiveData列表时,我需要获取Observer事件。但据我所知,只有当我用新的列表替换旧列表时才会收到事件。 例如,当我做下一个时:

list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))

观察者获取事件。但是当我只是将item添加到value时,Observer是静默的。 您能否就我如何实施我的需求提出建议?

9 个答案:

答案 0 :(得分:25)

在内部,LiveData会将每个更改跟踪为版本号(存储为int的简单计数器)。调用setValue()会增加此版本并使用新数据更新任何观察者(仅当观察者的版本号小于LiveData的版本时)。

启动此过程的唯一方法是调用setValue()或postValue()。副作用是如果LiveData的底层数据结构发生了变化(例如向Collection中添加元素),则不会发生任何事情将其传达给观察者。

因此,在向列表中添加项目后,您必须调用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自动化:)我建议这样做。

答案 1 :(得分:11)

我使用Kotlin Extension Function使其更容易:

fun <T> MutableLiveData<T>.notifyObserver() {
    this.value = this.value
}

然后在任何MutableLiveData中使用它,如下所示:

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.notifyObserver()
}

答案 2 :(得分:4)

LiveData仅在其包装对象引用发生更改时通知。当您将新的List分配给LiveData时,它将通知,因为其包装对象引用已更改,但是如果从LiveData的{​​{1}}添加/删除项目,它将不通知,因为它仍然具有与包装对象相同的List引用。因此,您可以通过如下扩展List来解决此问题:

MutableLiveData

然后从您的fun <T> MutableLiveData<MutableList<T>>.addNewItem(item: T) { val oldValue = this.value ?: mutableListOf() oldValue.add(item) this.value = oldValue } fun <T> MutableLiveData<MutableList<T>>.addNewItemAt(index: Int, item: T) { val oldValue = this.value ?: mutableListOf() oldValue.add(index, item) this.value = oldValue } fun <T> MutableLiveData<MutableList<T>>.removeItemAt(index: Int) { if (!this.value.isNullOrEmpty()) { val oldValue = this.value oldValue?.removeAt(index) this.value = oldValue } else { this.value = mutableListOf() } } 添加/删除项目,例如:

MutableLiveData

答案 3 :(得分:3)

我为 Kotlin 找到了更好的解决方案:

class MutableListLiveData<T>(
        private val list: MutableList<T> = mutableListOf()
) : MutableList<T> by list, LiveData<List<T>>() {

    override fun add(element: T): Boolean =
            element.actionAndUpdate { list.add(it) }

    override fun add(index: Int, element: T) =
            list.add(index, element).also { updateValue() }

    override fun addAll(elements: Collection<T>): Boolean =
            elements.actionAndUpdate { list.addAll(elements) }

    override fun addAll(index: Int, elements: Collection<T>): Boolean =
            elements.actionAndUpdate { list.addAll(index, it) }

    override fun remove(element: T): Boolean =
            element.actionAndUpdate { list.remove(it) }

    override fun removeAt(index: Int): T =
            list.removeAt(index).also { updateValue() }

    override fun removeAll(elements: Collection<T>): Boolean =
            elements.actionAndUpdate { list.removeAll(it) }

    override fun retainAll(elements: Collection<T>): Boolean =
            elements.actionAndUpdate { list.retainAll(it) }

    override fun clear() =
            list.clear().also { updateValue() }

    override fun set(index: Int, element: T): T =
            list.set(index, element).also { updateValue() }

    private fun <T> T.actionAndUpdate(action: (item: T) -> Boolean): Boolean =
            action(this).applyIfTrue { updateValue() }

    private fun Boolean.applyIfTrue(action: () -> Unit): Boolean {
        takeIf { it }?.run { action() }
        return this
    }

    private fun updateValue() {
        value = list
    }
}

这种实现的优点是您可以在 MutableListLiveData 上使用 Kotlin 扩展函数。然后你可以像这样使用它,你的 LiveData 会自动更新:

private val _items = MutableListLiveData<Item>()
val items: LiveData<List<Item>> = _items

fun addItem(item: Item) {
   _items.add(item)
}

答案 4 :(得分:1)

怎么样?

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();
        }
    });

答案 5 :(得分:1)

晚些时候参加聚会,但这是Gnzlt答案的更简洁版本,其中包含空检查以及可变列表和不可变列表:

// for mutable list
operator fun <T> MutableLiveData<MutableList<T>>.plusAssign(item: T) {
    val value = this.value ?: mutableListOf()
    value.add(item)
    this.value = value
}

// for immutable list
operator fun <T> MutableLiveData<List<T>>.plusAssign(item: T) {
    val value = this.value ?: emptyList()
    this.value = value + listOf(item)
}

在您的代码中:

list -= IssuePost(UserEntity(name, email, photoUrl), issueEntity))

答案 6 :(得分:0)

这是我的解决方案,它受到@ 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()

}

答案 7 :(得分:0)

我遇到了同样的问题,并决定在各处添加“值=值”或对另一个方法的调用太麻烦了,而且容易出错。

因此,我创建了自己的用于包装集合的类。现在,Observe将观察集合的内容,而不是集合本身。

该代码可在GitHub上找到: ObservableCollections

这是第一个版本,所以请原谅。 它尚未可用于gradle include,因此您必须下载它并将其作为库模块添加到您的应用程序中。

目前,它包括以下集合:

ArrayDeque
ArrayList
LinkedList
Queue
Stack
Vector

将在时间允许的情况下添加更多内容。 随时要求特定的。

请报告任何问题。

答案 8 :(得分:0)

我认为本课程将为您提供帮助:

class ArrayListLiveData<T> : MutableLiveData<ArrayList<T>>()
{
    private val mArrayList = ArrayList<T>()

    init
    {
        set(mArrayList)
    }

    fun add(value: T)
    {
        mArrayList.add(value)
        notifyChanged()
    }

    fun add(index: Int, value: T)
    {
        mArrayList.add(index, value)
        notifyChanged()
    }

    fun addAll(value: ArrayList<T>)
    {
        mArrayList.addAll(value)
        notifyChanged()
    }

    fun setItemAt(index: Int, value: T)
    {
        mArrayList[index] = value
        notifyChanged()
    }

    fun getItemAt(index: Int): T
    {
        return mArrayList[index]
    }

    fun indexOf(value: T): Int
    {
        return mArrayList.indexOf(value)
    }

    fun remove(value: T)
    {
        mArrayList.remove(value)
        notifyChanged()
    }

    fun removeAt(index: Int)
    {
        mArrayList.removeAt(index)
        notifyChanged()
    }

    fun clear()
    {
        mArrayList.clear()
        notifyChanged()
    }

    fun size(): Int
    {
        return mArrayList.size
    }
}

及其扩展名:

fun <T> MutableLiveData<T>.set(value: T)
    {
        if(AppUtils.isOnMainThread())
        {
            setValue(value)
        }
        else
        {
            postValue(value)
        }
    }

    fun <T> MutableLiveData<T>.get() : T
    {
        return value!!
    }

    fun <T> MutableLiveData<T>.notifyChanged()
    {
        set(get())
    }