当 ViewModel 通过 LiveData 公开时,如何对 UiModel 中的更改做出反应?
How to react on changes within a UiModel when it's exposed via LiveData by the ViewModel?
我看了 Florina Muntenescu 在 KontlinConf 2018 上的精彩演讲,她谈到了他们如何重塑他们的应用架构。
谈话的一部分是他们如何通过 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 属性 中有 text 时,我可以像这样使用 MediatorLiveData:
myMediatorLiveData.addSource(text){
// do something when text changed
}
但是当使用上面的方法时,UiModel 并没有改变,而是它的值被改变了。所以这在这里不起作用:
myMediatorLiveData.addSource(uiModel){
// do something when text inside uiModel changed
}
所以我的问题是如何使用这种方法对 ViewModel 中 UiModel 内部的更改做出反应?
感谢您的建议,
克里斯
我想总结一下我对上述主题的研究。
正如@CommonsWars 在评论中所说 you can implement the field in the UiModel as ObservableField
s. But after some hands on I currently prefer the approach described 。
这让我得到了以下代码:
ViewModel:
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 的属性。
我们可以通过以下方式 set/get ViewModel 中的 username
:
fun doSomething(){
uiModel.value!!.username = "abc"
}
fun doSomething(){
uiModel.value!!.username
}
当您通过 ObservableFields 实现它时,您必须 set/get username
通过:
fun doSomething(){
uiModel.value!!.username.set("abc")
}
fun doSomething(){
uiModel.value!!.username.get()
}
您可以选择最适合您需求的方法!
希望这对某人有所帮助。
克里斯
我看了 Florina Muntenescu 在 KontlinConf 2018 上的精彩演讲,她谈到了他们如何重塑他们的应用架构。
谈话的一部分是他们如何通过 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 属性 中有 text 时,我可以像这样使用 MediatorLiveData:
myMediatorLiveData.addSource(text){
// do something when text changed
}
但是当使用上面的方法时,UiModel 并没有改变,而是它的值被改变了。所以这在这里不起作用:
myMediatorLiveData.addSource(uiModel){
// do something when text inside uiModel changed
}
所以我的问题是如何使用这种方法对 ViewModel 中 UiModel 内部的更改做出反应?
感谢您的建议, 克里斯
我想总结一下我对上述主题的研究。
正如@CommonsWars 在评论中所说 ObservableField
s. But after some hands on I currently prefer the approach described
这让我得到了以下代码:
ViewModel:
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 的属性。
我们可以通过以下方式 set/get ViewModel 中的 username
:
fun doSomething(){
uiModel.value!!.username = "abc"
}
fun doSomething(){
uiModel.value!!.username
}
当您通过 ObservableFields 实现它时,您必须 set/get username
通过:
fun doSomething(){
uiModel.value!!.username.set("abc")
}
fun doSomething(){
uiModel.value!!.username.get()
}
您可以选择最适合您需求的方法! 希望这对某人有所帮助。
克里斯