簡體   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