android - 如何避免 MediatorLiveData 的所有源调用同一函数?

标签 android android-livedata android-jetpack event-driven

问题简介: 当使用 android 随 android jetpack 添加的 MediatorLiveData 时,我发现自己经常从各个来源调用相同的函数。例如,这可能是因为,每当更新源时,我都必须检查它是否有影响,或者另一个源是否更重要。代码示例(Kotlin,但不重要):

val isHovered = MutableLiveData<Boolean>()
val isSelected = MutableLiveData<Boolean>()

val color = MediatorLiveData<Int>().apply { 
    addSource(isHovered) { updateColor() }
    addSource(isSelected) { updateColor() }
}

fun updateColor() {
    if (isHovered.value == true)
        color.value = Color.GREEN
    else if (isSelected.value == true)
        color.value = Color.RED
    else
        color.value = Color.GRAY
}

项目悬停时为绿色,选择且未悬停时为红色,否则为灰色。当 isSelected 更改为 true 时,在将颜色更改为红色之前,我仍然需要检查它是否悬停。另外,当 isHovering 更改为 false 时,我需要在将颜色更改为灰色之前检查它是否被选中。因此,最简单的是一个函数,它考虑所有变量并相应地设置颜色。

我的问题: 当 MediatorLiveData 从非 Activity 状态更改为 Activity 状态时,由于 View 移至前台,因此可能会针对每个发生更改的源多次调用函数 updateColor。这是不必要的,因为每次调用都已经考虑了所有变量。由于此函数可能非常复杂并且可能有很多源,有没有办法避免对源 LiveDatas 的相同状态多次调用它?

最佳答案

我遇到了同样的问题并提出了以下解决方案。首先,将所有来源的值收集到一个值:

data class State(val isHovered: Boolean, val isSelected: Boolean)

private val state = MediatorLiveData<State>().apply {
    fun update() {
        value = State(isHovered.value ?: false, isSelected.value ?: false)
    }

    addSource(isHovered) { update() }
    addSource(isSelected) { update() }
}

然后我创建了一个扩展函数来仅发出不同的值

fun <T> LiveData<T>.distinct(): LiveData<T> = MediatorLiveData<T>().apply {
    var wasSet = false
    addSource(this@distinct) {
        // Update value if it has been changed or it's the first time the method called
        // Because value is null by default it will not be set to null again, leaving the live data in [value not set] state
        if (value != it || !wasSet) {
            value = it
            wasSet = true
        }
    }
}

因此,当观察 state.distinct() 时,只有当它真正发生变化时,您才会获得更新的值。

有时另一个扩展也可能有用:

fun <T> LiveData<T>.squashUpdates() = MediatorLiveData<T>().apply {
    addSource(this@squashUpdates) {
        postValue(it)
    }
}

通常,当 LiveData 值发生更改时,它会立即通知同一堆栈帧中的所有观察者。假设您有一个 MediatorLiveData 并更改多个源的值,例如:

isHovered.value = true
isSelected.value = true

state值将连续改变两次State(true, false)State(true, true)。即使使用上面的 distinct ,它们都会被调度,因为值实际上是不同的。在这种情况下,squashUpdates 通过延迟调度到堆栈帧的末尾、仅调度最后一个值来提供帮助。

关于android - 如何避免 MediatorLiveData 的所有源调用同一函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57256018/

相关文章:

android - Jetpack 撰写 : How to detect what is causing a Composable to recompose

android - 导航 Controller 在实时数据观察器中被调用两次

android - 是否可以更改导航组件中的向上按钮图标?

Android Google Map api v2 返回 null?

android - 如何以编程方式重命名 Android 应用程序?

android - 如何创建发出单个事件并仅通知最后订阅的观察者的 LiveData?

android - 如何设置 LiveData 以根据多个参数执行搜索?

java - 底部导航显示在其他 fragment 中

android - 布局重力不适用于LayoutParams

android - 捕获所有强制关闭并使用 Google Analytics 记录