Notify Observer when item is added to List of Live

2019-03-18 03:32发布

问题:

I need to get Observer event when item is added to List of LiveData. But as fa as I understand event receives only when I replace the old list with 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. Could you please give an advice how I can implement what I need?

回答1:

Internally, LiveData keeps track of each change as a version number (simple counter stored as an 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).

It appears the only way to start this process is by calling setValue() or 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.

Thus, you will have to call setValue() after adding an item to your list. I have provided two ways you could approach this below. I like the first option better.

Option 1

Keep the list outside of the LiveData and update with the reference any time the list contents change.

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

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

Option 2

Keep track of the list via the LiveData and update the LiveData with its own value whenever the list contents change.

private val mIssuePostLiveData = MutableLiveData<List<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.



回答2:

I use a Kotlin Extension Function to make it easier:

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

Then use it in any MutableLiveData like this:

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


回答3:

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


回答4:

Inspired by @user3682351 here is my solution. It seems that we cannot update properties of a LiveData value individually so the value must be updated on each operation. This is essentially a small wrapper on live data with convenience methods for modifying properties of a 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()

}