Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 450
Felipe Ribeiro R. Magalhaes Avatar asked Jun 15 '18 13:06

Felipe Ribeiro R. Magalhaes


People also ask

Which code is correct to create custom adapters in data binding?

To create a custom binding adapter, you need to create an extension function of the view that will use the adapter. Then, you add the @BindingAdapter annotation. You have to indicate the name of the view attribute that will execute this adapter as a parameter in the annotation.

How is data binding used in two-way binding?

Two-way data binding refers to sharing data between a component class and its template. If you change data in one place, it will automatically reflate at the other end. For example, if you change the value of the input box, then it will also update the value of the attached property in a component class.

What is 2 way data binding in Android?

Two-way data binding is nothing but updating the data source if there are any changes in the layout and vice versa. Two-way data binding is not applicable for all the views in Android. For example, using two-way data binding in EditText makes sense because users can update the data in the view.

What is the advantage of implementing LiveData and data binding?

The advantages of using LiveData Using LiveData provides the following advantages: Ensures your UI matches your data state. LiveData follows the observer pattern. LiveData notifies Observer objects when underlying data changes.


2 Answers

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 */ } }
like image 137
EpicPandaForce Avatar answered Sep 27 '22 02:09

EpicPandaForce


@EpicPandaForce solution is proper but in EditText two way binding can be obtained in much simpler way. Add attribute afterTextChanged to your widget as below:

android:afterTextChanged="@{viewModel::doLogic}"

Then in your ViewModel class just write method:

fun doLogic(s: Editable) { //update Livedata or do other logic }

EDIT

I have missed important documentation note. Much easier (and far more proprer) will be:

android:text="@={viewModel.someLivedata}

and then in our LifecycleOwner class we can update value of liveData everywhe when we need, and of course we can react on changes from registered observer.

like image 45
Karol Kulbaka Avatar answered Sep 23 '22 02:09

Karol Kulbaka