我需要在LiveData列表中添加项目时获得Observer事件。 但据我所知,只有在我用新列表替换旧列表时才会接收到该事件。 例如,当我执行以下操作时:
list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))
观察者收到了事件。但当我只是向值中添加项目时,观察者保持沉默。 请问你能给我建议如何实现我需要的功能吗?
我需要在LiveData列表中添加项目时获得Observer事件。 但据我所知,只有在我用新列表替换旧列表时才会接收到该事件。 例如,当我执行以下操作时:
list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))
观察者收到了事件。但当我只是向值中添加项目时,观察者保持沉默。 请问你能给我建议如何实现我需要的功能吗?
LiveData内部会使用版本号(一个简单的 int 类型计数器)跟踪每个更改。调用 setValue() 会递增该版本号,并仅在观察者的版本号小于 LiveData 的版本号时,更新任何观察者的新数据。
似乎唯一启动此过程的方法是调用 setValue() 或 postValue()。其副作用是,如果 LiveData 的基础数据结构发生了更改(例如将元素添加到 Collection 中),则不会向观察者传达这一点。
因此,在向列表添加项目后,您需要调用 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 的 扩展函数
来使它更容易:
fun <T> MutableLiveData<T>.notifyObserver() {
this.value = this.value
}
然后像这样在任何MutableLiveData
中使用它:
fun addIssuePost(issuePost: IssuePost) {
mIssuePostLiveData.value?.add(issuePost)
mIssuePostLiveData.notifyObserver()
}
fun <T> MutableLiveData<List<T>>.add(item: T) { val updatedItems = this.value?.toMutableList() ?: mutableListOf() updatedItems.add(item) this.value = updatedItems }
- musooff虽然有些晚,但这里给出一个更简洁的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
仅在其包装的对象引用发生变化时才会通知。当您将一个新的 List
分配给 LiveData
时,它将通知因为其包装的对象引用已经改变,但是如果从 LiveData
的 List
中添加/删除项目,则不会通知,因为它仍然具有相同的 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))
ViewModel
获取 LiveData
的值:1. 您可以观察该 LiveData
,2. 您可以在 ViewModel
或您的 Activity
中编写一个返回该 LiveData
值的函数,3. 您可以使用另一个 LiveData
发出该 LiveData
的值。如果您还不清楚,请提出描述您的问题并在评论部分提及我,我会在那里为您提供帮助。 - Md. Yamin Mollah我为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)
}
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();
}
});
ArrayDeque
ArrayList
LinkedList
Queue
Stack
Vector
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
}
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())
}