【问题标题】:How to react on changes within a UiModel when it's exposed via LiveData by the ViewModel?当 ViewModel 通过 LiveData 公开 UiModel 中的更改时,如何对其做出反应?
【发布时间】:2020-02-23 12:30:34
【问题描述】:

我在 KontlinConf 2018 上观看了 Florina Muntenescu 的精彩演讲,她谈到了他们如何重塑应用架构。

谈话的一部分是他们如何通过来自 ViewModel 的 LiveData 公开 UiModel(不是 ViewModel)。 (watch here)

她做了一个类似这样的例子:

class MyViewModel constructor(...) : ViewModel() {

    private val _uiModel = MutableLiveData<UiModel>()
    val uiModel: LiveData<UiModel>
        get() = _uiModel
}

上面 ViewModel 的视图声明可以是:

<layout>
    <data>
        <variable
            name="viewModel"
            type="com.demo.ui.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout>

        <EditText
            android:id="@+id/text"
            android:text="@={viewModel.uiModel.text}" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

她没有谈到(或者我错过了)他们如何对 UiModel 本身的属性变化做出反应。 如何在每次文本更改时执行函数?

text 在 ViewModel 中的单独 LiveData 属性中时,我可以使用 MediatorLiveData,例如:

myMediatorLiveData.addSource(text){
   // do something when text changed
}

但是当使用上面的方法时,UiModel 并没有改变,而是它的值被改变了。所以这在这里不起作用:

myMediatorLiveData.addSource(uiModel){
   // do something when text inside uiModel changed
}

所以我的问题是如何使用这种方法对 ViewModel 中的 UiModel 内的变化做出反应?

感谢您的建议, 克里斯

【问题讨论】:

  • “但是当使用上面的方法时,UiModel 并没有改变,而是它的值被改变了”——这段代码中没有任何暗示。如果有的话,我希望情况并非如此。恕我直言,UiModel 应该是带有val 属性的data class,因此它是不可变的,并且当text 需要更改时,应该通过LiveData 发布一个新的UiModel 实例。
  • 感谢您的回复。当您查看声明的视图时,您会注意到“android:text="@={viewModel.uiModel.text" 这意味着 UiModel 中的“文本”发生了变化。在这种情况下,我无法对变化做出反应,也无法't 整体交换 UiModel。对吗?!
  • 没错,他们使用的是双向绑定。我注意到了这一点。就个人而言,我不喜欢双向数据绑定,所以我没有考虑这一点。在这种情况下,text 需要单独观察,例如使用the data binding ObservableField class。如果是这样,您可以自己观察text

标签: android android-livedata android-architecture-components android-viewmodel


【解决方案1】:

我想总结一下我对上述主题的研究。

正如@CommonsWars 在 cmets above 中所说,您可以将 UiModel 中的字段实现为 ObservableFields。但经过一些操作后,我目前更喜欢here 描述的方法。

这导致我得到以下代码:

视图模型:

class MyViewModel constructor(...) : ViewModel() {
   val uiModel = liveData{
       val UiModel uiModel = UiModel() // get the model from where ever you want
       emit(uiModel)
   }

   fun doSomething(){
      uiModel.value!!.username = "abc"
   }
}

UiModel

class UiModel : BaseObservable() {

   @get:Bindable // puts '@Bindable' on the getter
   var text = ""
      set(value) {
         field = value
         notifyPropertyChanged(BR.text) // trigger binding
      }

   val isValid: Boolean
   @Bindable("text") get() { // declare 'text' as a dependency
      return !text.isBlank()
   }
}

布局

<layout>
    <data>
        <variable
            name="viewModel"
            type="com.demo.ui.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout>

        <EditText
            android:id="@+id/text"
            android:text="@={viewModel.uiModel.text}" />

        <Button
            android:id="@+id/sent_btn"
            android:enabled="@{viewModel.uiModel.isValid}" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我选择这种方法是因为我们在 ViewModel 中更改 UiModel 属性的方式。

您可以通过以下方式在 ViewModel 中设置/获取username

fun doSomething(){
   uiModel.value!!.username = "abc"
}

fun doSomething(){
   uiModel.value!!.username
}

当您通过 ObservableFields 实现它时,您必须通过以下方式设置/获取 username

fun doSomething(){
   uiModel.value!!.username.set("abc")
}

fun doSomething(){
   uiModel.value!!.username.get()
}

您可以选择最适合您需求的方法! 希望这对某人有所帮助。

克里斯

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 2016-08-09
    相关资源
    最近更新 更多