将项目添加到LiveData列表时通知观察者

Rus*_*nko 35 android kotlin android-livedata

将项目添加到LiveData列表时,我需要获取Observer事件.但正如我所理解的那样,只有当我用新的列表替换旧列表时才会收到事件.例如,当我做下一个时:

list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))
Run Code Online (Sandbox Code Playgroud)

观察者得到事件.但是当我只是将item添加到value时,Observer是静默的.您能否就我如何实施我的需求提出建议?

Pro*_*001 36

在内部,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
}
Run Code Online (Sandbox Code Playgroud)

选项2

通过LiveData跟踪列表,并在列表内容更改时使用自己的值更新LiveData.

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

init {
   mIssuePostLiveData.value = ArrayList()
}

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.value = mIssuePostLiveData.value
}
Run Code Online (Sandbox Code Playgroud)

这些解决方案中的任何一个都应该帮助您每次修改当前列表时都必须创建一个新列表,以通知观察者.


Gnz*_*zlt 30

我使用Kotlin Extension Function使其变得更容易:

fun <T> MutableLiveData<T>.notifyObserver() {
    this.value = this.value
}
Run Code Online (Sandbox Code Playgroud)

然后以任何MutableLiveData类似的方式使用它:

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.notifyObserver()
}
Run Code Online (Sandbox Code Playgroud)

  • 更好的方法`fun &lt;T&gt; MutableLiveData &lt;List &lt;T &gt;&gt;。add(item:T){val UpdatedItems = this.value as ArrayList UpdatedItems.add(item)this.value = UpdatedItems}` (12认同)
  • 如果您从后台线程使用notifyObserver(),应用程序将崩溃。如果您从非 UI 线程发出通知,请使用 this.postValue(this.value)。 (6认同)
  • @musooff,就我而言,这导致了崩溃。这就是为什么,我对你的代码做了一些更改。我使用 MutableLiveData&lt;MutableList&lt;T&gt;&gt; 而不是 MutableLiveData&lt;List&lt;T&gt;&gt; (4认同)
  • Gnzlt 的原始解决方案是最好的。它更通用,因为它将数据类型与通知分开([关注点分离](https://en.wikipedia.org/wiki/Separation_of_concerns))。 (3认同)

Ehs*_*veh 12

迟到了,但这里有一个更简洁的 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)
}
Run Code Online (Sandbox Code Playgroud)

在你的代码中:

list += IssuePost(UserEntity(name, email, photoUrl), issueEntity))
Run Code Online (Sandbox Code Playgroud)


Md.*_*lah 9

LiveData只会在其包装的对象引用更改时发出通知。当你分配一个新ListLiveData,然后它会通知,因为它包装的对象引用更改,但如果从添加/删除项目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()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后添加/删除您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))
Run Code Online (Sandbox Code Playgroud)


Ven*_*min 7

我找到了一个更好的 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
    }
}
Run Code Online (Sandbox Code Playgroud)

这种实现的优点是您可以在MutableListLiveData. 然后你可以像这样使用它,你的LiveData会自动更新:

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

fun addItem(item: Item) {
   _items.add(item)
}
Run Code Online (Sandbox Code Playgroud)