向 LiveData 列表添加项时通知观察者

当该项被添加到 LiveData 列表时,我需要获取观察者事件。但据我所知,事件只有在我用新的列表替换旧的列表时才会接收到。 例如,当我做下一个:

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

观察者得到事件。但是当我只添加项目的价值,观察者是沉默的。 你能给我一些关于如何实现我所需要的建议吗?

87964 次浏览

在内部,LiveData 将每次更改记录为版本号(简单的计数器存储为 int)。调用 setValue ()增加这个版本并用新数据更新任何观察者(只有当观察者的版本号小于 LiveData 的版本号时)。

似乎启动此进程的唯一方法是调用 setValue ()或 postValue ()。其副作用是,如果 LiveData 的底层数据结构发生了变化(比如向 Collection 添加了一个元素) ,那么将不会发生任何事情来向观察者传达这一点。

因此,在向列表中添加项之后,必须调用 setValue ()。我在下面提供了两种方法来处理这个问题。

选择一

将列表保留在 LiveData 之外,并在列表内容更改时使用引用进行更新。

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


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

选择二

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

这个怎么样?

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

受@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()


}


虽然已经迟到了,但这里有一个更简洁的 Gnzlt 回答版本,包括 null-check 和可变和不可变列表:

// 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只会在其已包装的对象引用更改时发出通知。当你分配一个新的 List到一个 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))

我认为这门课会对你有所帮助:

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

我为科特林找到了一个更好的解决办法:

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

这个实现的优点是你可以在 abc0上使用 Kotlin 扩展函数。然后你可以像这样使用它,你的 LiveData 会自动更新:

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


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

我想出了我自己的解决方案(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
}