Live Data and 2-Way Data Binding: Custom setter not being called

Felipe Ribeiro R. Magalhaes picture Felipe Ribeiro R. Magalhaes · Jun 15, 2018 · Viewed 10.1k times · Source

I am using 2-way data binding to update a LiveData String object from my ViewModel with a string set in the EditText:

<android.support.design.widget.TextInputEditText
  android:id="@+id/writeReviewTitle"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="@={viewModel.liveReviewTitle}"
/>

So, from my understanding, the ViewModel would have its liveReviewTitle attribute updated every time the text changed in the EditText. I assume this is happening through the usage of a TextWatcher or some sort of listening mechanism that is being taken care of for me by the library. I also thought that when the text needed to be updated, it would have its setter called. Which does not seem to be the case! When the text changes, I need to do some more stuff in my ViewModel, therefore I implemented a custom setter for liveReviewTitle, but it is not being called (I tried debugging). This is how it looks like in the ViewModel class:

var liveReviewTitle: MutableLiveData<String> = MutableLiveData()
        set(value) {
            field = value
            customLogicHere()
        }

Tried debugging this setter but it never seems to be called! What is happening here? Feels a little confusing. The text is being updated, and it is saved in the ViewModel, it is just the setter that is not called.

Answer

EpicPandaForce picture EpicPandaForce · Jun 15, 2018

Of course it's never called, you're not setting a new MutableLiveData, you're setting a new String value inside the MutableLiveData (possibly with setValue).

However, you should be able to intercept the value that's being set and execute custom logic after setting the value if you expose a MediatorLiveData instead of the MutableLiveData directly.

EDIT: the following should work as expected:

val liveReviewTitle: MutableLiveData<String> = MutableLiveData()
private val mediator = MediatorLiveData<String>().apply {
    addSource(liveReviewTitle) { value ->
        setValue(value)
        customLogicHere()
    }
}.also { it.observeForever { /* empty */ } }