向 MediatorLiveData 添加多个源并更改其值

Add multiple source to MediatorLiveData and change its value

基本上我有一个屏幕,还有几个EditText和一个Button。 用户必须填写所有字段,否则 Button 将被禁用。 我正在使用 DataBinding 来实现这一点。下面是我在视图模型中的代码。

val isNextEnabled = MediatorLiveData<Boolean>()
isNextEnabled.apply {
            addSource(field1LiveData) {
                isNextEnabled.value =
                    it != null
                            && field2LiveData.value != null
                            && field3LiveData.value != null
            }
            addSource(field2LiveData) {
                isNextEnabled.value =
                    it != null
                            && field1LiveData.value != null
                            && field3LiveData.value != null
            }
            addSource(field3LiveData) {
                isNextEnabled.value =
                    it != null
                            && field2LiveData.value != null
                            && field1LiveData.value != null
            }
        }

在xml

<Button
    android:enabled="@{viewmodel.isNextEnabled}"
    .
    .
    .
</Button>

一切正常,如预期的那样。但是上面的逻辑看起来很繁琐。如果我有更多 EditText 怎么办? write/maintain.

代码会很痛苦

有什么办法可以简化它吗?

最终您有一个 UseCase/Logic,其中 决定何时启用 next 按钮。

我认为您应该将逻辑分离到有意义的用例中。

例如

// update these when they change in the UI for e.g.
    val field1Flow: Flow<Boolean> = flow { ... }
    val field2Flow: Flow<Boolean> = flow { ... }
    
    
    val nextButtonState = combine(field1Flow, field2Flow) { f1, f2 -> 
        f1 && f2
    }.collect { state -> 
        // use your state.
 }

现在...如果您需要 特殊 逻辑而不仅仅是 two-boolean 代数,您可以随时将其提取到 use-cases 中 return 更多流量。

map它或您可以执行的各种操作:

例如

class YourUseCase() {

   operator fun invoke(field1: Boolean, field2: Boolean) {
      // Your Logic
      return field1 && field2 
   } 
}

// And now...
val _nextButtonState = combine(field1Flow, field2Flow) { f1, f2 -> 
    YourUseCase(f1, f2)
}

val _uiState = _nextButtonState.transformLatest { 
   emit(it) // you could add a when(it) { } and do more stuff here
}

// And if you don't want to change your UI to use flows, you can expose this as live data
 val uiState = _uiState.asLiveData()

请记住,这是 Pseudo-code 写在 SO.. 上的,甚至记事本也不行 ;)

我希望这有点道理。这个想法是将位分成 use-cases (您最终可以单独测试)并拥有数据流。当按钮改变状态时,fieldNFlow 发出值,这会为您触发整个链。

如果您有最新的协程 (2.4.0+),您可以使用新的运算符来避免使用 LiveData,但总的来说,我会尝试朝这个方向思考。

最后,你的带有调解器的 liveData 代码还不错,我至少将“逻辑”提取到 3 个不同的用例中,这样它们就不会全部放在一系列 if/else 语句中。

提醒一句:我已经超过 3(?)年没有使用数据绑定了,我个人不是它的粉丝所以我不能告诉你它是否会导致这种方法出现问题。