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