Notify Observer when item is added to List of LiveData

后端 未结 8 1193
一个人的身影
一个人的身影 2020-12-07 20:21

I need to get an Observer event when the item is added to the List of LiveData. But as far as I understand the event receives only when I replace the old list with a new one

相关标签:
8条回答
  • 2020-12-07 20:22

    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.

    So, I created my own classes that wrap the collections. Observe will now observe the contents of the collection instead of the collection itself.

    The code is available on GitHub at: 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.

    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. Again, see there for details.

    0 讨论(0)
  • 2020-12-07 20:28

    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()
    
    }
    
    
    0 讨论(0)
  • 2020-12-07 20:30

    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.

    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<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. This is essentially Option 2 automated :) I would recommend doing that.

    0 讨论(0)
  • 2020-12-07 20:30

    LiveData will only notify when its wrapped object reference is changed. 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. So you can overcome this problem by making an extension of MutableLiveData as follows:

    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:

    // 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))
    
    0 讨论(0)
  • 2020-12-07 20:36

    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:

    // 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))
    
    0 讨论(0)
  • 2020-12-07 20:43

    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()
    }
    
    0 讨论(0)
提交回复
热议问题