简体   繁体   English

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

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

I need to get an Observer event when the item is added to the List of LiveData.当项目被添加到 LiveData 列表时,我需要获得一个观察者事件。 But as far as I understand the event receives only when I replace the old list with a new one.但据我所知,只有当我用新列表替换旧列表时才会收到事件。 For example when I do the next:例如,当我执行以下操作时:

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

Observer gets event.观察者获取事件。 But when I just add item to value, Observer is silent.但是当我只是将 item 添加到 value 时,Observer 是沉默的。 Could you please give me advice on how I can implement what I need?您能否就如何实现我的需要给我建议?

Internally, LiveData keeps track of each change as a version number (simple counter stored as an int).在内部,LiveData 将每个更改作为版本号(存储为 int 的简单计数器)进行跟踪。 Calling setValue() increments this version and updates any observers with the new data (only if the observer's version number is less than the LiveData's version).调用 setValue() 会增加此版本并使用新数据更新任何观察者(仅当观察者的版本号小于 LiveData 的版本时)。

It appears the only way to start this process is by calling setValue() or postValue().似乎启动此过程的唯一方法是调用 setValue() 或 postValue()。 The side-effect is if the LiveData's underlying data structure has changed (such as adding an element to a Collection), nothing will happen to communicate this to the observers.副作用是如果 LiveData 的底层数据结构发生了变化(例如向集合中添加元素),则不会发生任何事情将其传达给观察者。

Thus, you will have to call setValue() after adding an item to your list.因此,您必须在将项目添加到列表后调用 setValue()。 I have provided two ways you could approach this below.我在下面提供了两种方法可以解决这个问题。

Option 1选项1

Keep the list outside of the LiveData and update with the reference any time the list contents change.将列表保留在 LiveData 之外,并在列表内容更改时随时更新引用。

private val mIssuePosts = ArrayList<IssuePost>()
private val mIssuePostLiveData = MutableLiveData<List<IssuePost>>()

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

Option 2选项 2

Keep track of the list via the LiveData and update the LiveData with its own value whenever the list contents change.通过 LiveData 跟踪列表并在列表内容更改时使用其自己的值更新 LiveData。

private val mIssuePostLiveData = MutableLiveData<MutableList<IssuePost>>()

init {
   mIssuePostLiveData.value = ArrayList()
}

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

Either of these solutions should help you from having to create a new list every time you modify the current list just to notify the observers.这些解决方案中的任何一个都应该可以帮助您避免每次修改当前列表时都必须创建一个新列表来通知观察者。

UPDATE:更新:

I've been using similar techniques for a while now as Gnzlt has mentioned in his answer to use a Kotlin extension function to assign the LiveData to itself to simplify the code.我已经使用类似的技术一段时间了,因为 Gnzlt 在他的回答中提到使用 Kotlin 扩展函数将 LiveData 分配给自身以简化代码。 This is essentially Option 2 automated :) I would recommend doing that.这基本上是选项 2 自动化 :) 我建议这样做。

I use a Kotlin Extension Function to make it easier:我使用 Kotlin Extension Function来简化它:

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

Then use it in any MutableLiveData like this:然后在任何MutableLiveData使用它,如下所示:

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

Late to the party but here is a more concise version of Gnzlt's answer with null-check and for both mutable and immutable lists:迟到了,但这里有一个更简洁的 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)
}

And in your code:在你的代码中:

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

LiveData will only notify when its wrapped object reference is changed. LiveData只会在其包装的对象引用发生更改时LiveData通知。 When you assign a new List to a LiveData then it will notify because its wrapped object reference is changed but if add/remove items from a LiveData 's List it will not notify because it still has the same List reference as wrapped object.当您将新List分配给LiveData它会LiveData通知,因为它的包装对象引用已更改,但如果从LiveDataList添加/删除项目,它不会通知,因为它仍然具有与包装对象相同的List引用。 So you can overcome this problem by making an extension of MutableLiveData as follows:所以你可以通过对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()
    }
}

Then add/remove items from your MutableLiveData like:然后从您的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))

How about this?这个怎么样?

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

I found a better solution for Kotlin:我为 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
    }
}

The advantage of this implementation is that you can use Kotlin extension functions on MutableListLiveData .这种实现的优点是您可以在MutableListLiveData上使用 Kotlin 扩展函数。 Then you can use it like this, and your LiveData automatically updates:然后你可以像这样使用它,你的 LiveData 会自动更新:

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

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

I ran into the same problem and decided that adding "value = value" or a call to another method everywhere was too much of a pain and very error-prone.我遇到了同样的问题,并决定在任何地方添加“value = value”或调用另一种方法太麻烦而且很容易出错。

So, I created my own classes that wrap the collections.所以,我创建了自己的类来包装集合。 Observe will now observe the contents of the collection instead of the collection itself. Observe 现在将观察集合的内容而不是集合本身。

The code is available on GitHub at: ObservableCollections代码可在 GitHub 上找到: ObservableCollections

This is a FIRST version so please excuse any problems.这是第一个版本,所以请原谅任何问题。 It is not yet available for gradle include so you will have to download it and add it as a library module to you application.它尚不可用于 gradle include,因此您必须下载它并将其作为库模块添加到您的应用程序中。

At present it includes the following collections:目前包括以下馆藏:

ArrayDeque
ArrayList
LinkedList
Queue
Stack
Vector

More will be added as time allows.如果时间允许,将添加更多。 Feel free to request specific ones.随意请求特定的。

And PLEASE report any problems.并请报告任何问题。

Edit: It now includes far more collections.编辑:它现在包含更多的集合。 See there for details.有关详细信息,请参阅那里。
Also, it is available for include via gradle.此外,它可用于通过 gradle 包含。 Again, see there for details.再次,请参阅那里的详细信息。

Inspired by @user3682351 here is my solution.受到@user3682351 的启发,这是我的解决方案。 It seems that we cannot update properties of a LiveData value individually so the value must be updated on each operation.似乎我们无法单独更新LiveData值的属性,因此必须在每次操作时更新该值。 This is essentially a small wrapper on live data with convenience methods for modifying properties of a HashMap这本质上是实时数据的一个小包装器,带有用于修改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()

}

I think this class will help you:我认为这门课会帮助你:

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

and this extensions:和这个扩展:

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

I came up with my own solution (Java), I created a custom MutableLiveData that keeps note of the last time the list was modified and notifies us accordingly:我想出了自己的解决方案(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);
    }
}

Usage:用法:

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