繁体   English   中英

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

[英]Notify Observer when item is added to List of LiveData

当项目被添加到 LiveData 列表时,我需要获得一个观察者事件。 但据我所知,只有当我用新列表替换旧列表时才会收到事件。 例如,当我执行以下操作时:

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

观察者获取事件。 但是当我只是将 item 添加到 value 时,Observer 是沉默的。 您能否就如何实现我的需要给我建议?

在内部,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<MutableList<IssuePost>>()

init {
   mIssuePostLiveData.value = ArrayList()
}

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

这些解决方案中的任何一个都应该可以帮助您避免每次修改当前列表时都必须创建一个新列表来通知观察者。

更新:

我已经使用类似的技术一段时间了,因为 Gnzlt 在他的回答中提到使用 Kotlin 扩展函数将 LiveData 分配给自身以简化代码。 这基本上是选项 2 自动化 :) 我建议这样做。

我使用 Kotlin Extension Function来简化它:

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

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

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

迟到了,但这里有一个更简洁的 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))

LiveData只会在其包装的对象引用发生更改时LiveData通知。 当您将新List分配给LiveData它会LiveData通知,因为它的包装对象引用已更改,但如果从LiveDataList添加/删除项目,它不会通知,因为它仍然具有与包装对象相同的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添加/删除项目,例如:

// Here is your IssuePost list
var issuePostList = MutableLiveData<MutableList<IssuePost>>()

// Add new item to your list
issuePostList.addNewItem(IssuePost(UserEntity(name, email, photoUrl), issueEntity))

// Delete an item from your list at position i
issuePostList.removeItemAt(i)

// Add new item to your list at position i
issuePostList.addNewItemAt(i, IssuePost(UserEntity(name, email, photoUrl), issueEntity))

这个怎么样?

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

我为 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)
}

我遇到了同样的问题,并决定在任何地方添加“value = value”或调用另一种方法太麻烦而且很容易出错。

所以,我创建了自己的类来包装集合。 Observe 现在将观察集合的内容而不是集合本身。

代码可在 GitHub 上找到: ObservableCollections

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

目前包括以下馆藏:

ArrayDeque
ArrayList
LinkedList
Queue
Stack
Vector

如果时间允许,将添加更多。 随意请求特定的。

并请报告任何问题。

编辑:它现在包含更多的集合。 有关详细信息,请参阅那里。
此外,它可用于通过 gradle 包含。 再次,请参阅那里的详细信息。

受到@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()

}

我认为这门课会帮助你:

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

我想出了自己的解决方案(Java),我创建了一个自定义的MutableLiveData ,它会记录上次修改列表的时间并相应地通知我们:

public class MutableListLiveData<T> extends MutableLiveData<List<T>> {
    private final MutableLiveData<Long> lastModified = new MutableLiveData<>();
    private List<T> items;
    private ListObserver<List<T>> callback;

    public MutableListLiveData() {
        this.items = new ArrayList<>();
    }

    public void addItem(T item) {
        items.add(item);
        onListModified();
    }

    public void removeItem(int position) {
        items.remove(position);
        onListModified();
    }

    public void updateItem(int position, T item) {
        items.set(position, item);
        onListModified();
    }

    public T getItem(int position) {
        return items.get(position);
    }

    private void onListModified() {
        lastModified.setValue(System.currentTimeMillis());
    }

    @Override
    public List<T> getValue() {
        return items;
    }

    @Override
    public void setValue(List<T> items) {
        this.items = items;
        onListModified();
    }

    public void observe(@NonNull LifecycleOwner owner, ListObserver<List<T>> callback) {
        this.callback = callback;
        lastModified.observe(owner, this::onListItemsChanged);
    }

    private void onListItemsChanged(long time) {
        if (callback != null) callback.onListItemsChanged(items, items.size());
    }

    public interface ListObserver<T> {
        void onListItemsChanged(T items, int size);
    }
}

用法:

MutableListLiveData<List<Integer>> myList = new MutableListLiveData<>();
myList.observe(owner, this::onListChanged(items, size);

private void onListChanged(List<Integer> items, int size) {
    // Do Something
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM