Как реагировать на изменения в UiModel, когда он отображается через LiveData ViewModel? - PullRequest
0 голосов
/ 23 февраля 2020

Я смотрел этот потрясающий разговор Флорины Мунтенеску на KontlinConf 2018, где она рассказывала о том, как они изменили архитектуру своего приложения.

Одна часть разговора была о том, как они раскрывают UiModel ( не ) ViewModel) через LiveData из ViewModel. ( смотреть здесь )

Она сделала пример, подобный этому:

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. Как я могу выполнять функцию каждый раз, когда текст изменяется?

Имея текст в отдельном свойстве LiveData в ViewModel, я мог бы использовать MediatorLiveData для это как:

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

Но при использовании подхода, описанного выше, UiModel не меняется, а изменяются его значения. Так что это здесь не работает:

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

Итак, мой вопрос как я могу реагировать на изменения внутри UiModel в ViewModel с помощью этого подхода?

Спасибо за совет, Крис

1 Ответ

0 голосов
/ 29 февраля 2020

Я хочу подвести итог моего исследования относительно топи c выше.

Как сказано в комментариях @CommonsWars выше , вы можете реализовать поле в UiModel как ObservableField S . Но после некоторых попыток я предпочитаю подход, описанный здесь .

. Это привело меня к следующему коду:

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>

Я выбрал этот подход из-за как мы изменяем свойства UiModel из ViewModel.

Мы можем установить / получить username в ViewModel следующим образом:

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()
}

Вы можете выбрать подход, который лучше всего подходит для ваших нужд! Надеюсь, это кому-нибудь поможет.

Крис

...