Igor Escodro
Igor Escodro

Reputation: 1339

Using LiveData with Data Binding

With the stabilization of Android Architecture Components I started updating all my basic ViewModels to the new implementation ofViewModel. In my understanding, the usage of LiveData is recommended to hold the Model class since it handles the lifecycle better.

I like using Data Binding because it makes the code clearer in Java/Kotlin side and it is not needed to "watch" the value changes to update the UI. However the layout using Data Binding only watch data changes if the Model (or the ViewModel) extends BaseObservable and LiveData does not. I understand the one of the main objectives of LiveData is to be observed and updates the UI programmatically but for simple updates, Data Binding is very useful.

This issue was already reported (GitHub and Stack Overflow) and first was said that the version 1.0 would have it and now is said that this feature is in development.

In order to use both LiveData and Data Binding, I created a very simple implementation of class that extends BaseObservable:

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import android.databinding.BaseObservable

class ObservableMutableLiveData<T>() : BaseObservable() {

    private var data: MutableLiveData<T> = MutableLiveData()

    constructor(data: T) : this() {
        this.data.value = data
    }

    public fun set(value: T) {
        if (value != data.value) {
            data.value = value
            notifyChange()
        }
    }

    public fun get(): T? {
        return data.value
    }

    public fun getObservable(): LiveData<T> {
        return data
    }
}

So basically my ObservableMutableLiveData is a copy of ObservableField using LiveData to store the model and with this implementation, the layout updates after every model update.

The questions are:

Upvotes: 31

Views: 19931

Answers (4)

Ali Kazi
Ali Kazi

Reputation: 1636

The accepted answer does not give an example. So here's one I've tested and it works.

In layout:

<layout>
    <data>
        <variable
            name="viewmodel"
            type="com.abc.xyz.viewmodels.MyViewModel"/>
    </data>

    ...

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{viewmodel.myString}'
        ...
     />

    ...

</layout>

In fragment:

override fun onCreateView(inflater: LayoutInflater, 
         container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val binding: FragmentAlbumBinding = DataBindingUtil.inflate(
            inflater, R.layout.fragment_album, container, false)
        binding.apply {
            fragment = this
            viewModel = albumViewModel
            lifecycleOwner = this
    }
}

Upvotes: 1

Sir Codesalot
Sir Codesalot

Reputation: 7283

For those who came across this question looking for an example like I did, here's one:

In layout .xml put the LiveData element with it's type:

<layout>
    <data>
        <variable
            name="myString"
            type="android.arch.lifecycle.MutableLiveData&lt;String&gt;"/>
    </data>

    ...

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{myString}'
        ...
     />

    ...

</layout>

In your code set it's value and the lifecycle owner:

MutableLiveData<String> myString = new MutableLiveData<>();

...

binding.setLifecycleOwner(this);
binding.setMyString(myString);

That's it :)

Note that the default value of LiveData elements is null so assign initial values to make sure you get the desired effect right away or use this to enforce nullability.

EDIT: Use androidx.lifecycle.MutableLiveData&lt;String&gt; as type if you use androidx.

Upvotes: 35

Serg Burlaka
Serg Burlaka

Reputation: 2496

For androidx will be:

androidx.lifecycle.MutableLiveData

<layout>
    <data>
        <variable
            name="myString"
            type="androidx.lifecycle.MutableLiveData&lt;String&gt;"/>
    </data>

    ...

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text='@{myString}'
        ...
     />

    ...

</layout>

And for Kotlin:

  val myStr = MutableLiveData<String>()

...

 binding.apply {
            setLifecycleOwner(this)
            this.myString = myStr
        }

Good luck! :)

Upvotes: 1

Igor Escodro
Igor Escodro

Reputation: 1339

The Android Studio 3.1 (currently in Canary 6) will fix this issue, since LiveData can be used as observable field:

Updates to Data Binding:

You can now use a LiveData object as an observable field in data binding expressions. The ViewDataBinding class now includes a new setLifecycle method that you need to use to use to observe LiveData objects.

Source: Android Studio 3.1 Canary 6 is now available

Upvotes: 15

Related Questions