Android Data Binding 101

April 18, 2020

Image from instagram

In previous article we discussed View Binding.

Here, we’ll get a jump start with Data Binding, which can be thought of as a powerful and sometimes a little complex extension to View Binding.

In View Binding, we just bind our xml objects (view widgets) with our views(Activity or Fragments). It helps us to make our code less verbose, more manageable and prevent running into Null pointer exceptions related to views.

In Data Binding, in addition to what we do in View Binding, we bind the data which is to be displayed by a particular widget directly into the xml.

Let’s take an example:

Let’s suppose we want to have an activity like this:

screen

We’ll populate the movie data and we’ll do it in the xml itself.

  • First step is enabling Data Binding. For this, put the following line in your app’s build.gradle file:
    android {
        ...
        dataBinding {
            enabled = true
        }
        ...
    }

  • Now we’ll create out ViewModel class MovieTitleViewModel.java
public class MovieTitleViewModel extends ViewModel {
    public MutableLiveData<MovieEntity> entity = new MutableLiveData<>();

    public MovieTitleViewModel() {
        MovieEntity temp = new MovieEntity();
        temp.setTitle("Something");
        temp.setFavourite(false);
        entity.setValue(temp);
    }

    public void favourite(){
        MovieEntity temp = entity.getValue();
        temp.setFavourite(!temp.isFavourite());
        entity.setValue(temp);
    }

    public void markWatched(){
        entity.getValue().setWatched(!entity.getValue().isWatched());
    }
    public void addToWatchlist(){
        entity.getValue().setAddedToWatchlist(!entity.getValue().isAddedToWatchlist());
    }
    public void setRating(int rating){
        MovieEntity temp = entity.getValue();
        temp.setRating(rating);
        entity.setValue(temp);
    }

    public LiveData<MovieEntity> getEntity(){
        return entity;
    }
}

Here we’re creating a MutableLiveData object of type MovieEntity.

MutableLiveData is simply and Observable which will update everyone listening to changes to our MovieEntity object. The data which will be binded in this example in the xml will be binded from our ViewModel class.

  • Our MovieEntity.java class is a POJO having the following definition:
package com.a22boxes.oscarswatch.model;

public class MovieEntity {
    String title;
    String directors;
    String writers;
    String synopsis;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDirectors() {
        return directors;
    }

    public void setDirectors(String directors) {
        this.directors = directors;
    }

    public String getWriters() {
        return writers;
    }

    public void setWriters(String writers) {
        this.writers = writers;
    }

    public String getSynopsis() {
        return synopsis;
    }

    public void setSynopsis(String synopsis) {
        this.synopsis = synopsis;
    }
}

  • To set our XML view up for DataBinding, we need to wrap the entire layout definition into a layout tag and define the variable and its type.

activity_movie.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.a22boxes.packagename.MovieTitleViewModel" />
    </data>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/watchlist">


        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/background"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">


            <ImageButton
                android:id="@+id/back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="15dp"
                android:background="@null"
                android:onClick="@{()->activity.finish()}"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/back"
                tools:ignore="VectorDrawableCompat" />

            <ImageButton
                android:id="@+id/fav"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:layout_marginEnd="20dp"
                android:layout_marginRight="20dp"
                android:background="@null"
                android:onClick="@{()->viewModel.favourite()}"
                android:src="@{ viewModel.entity.favourite ? @drawable/heart_selected : @drawable/heart_unselected, default=@drawable/heart_unselected }"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent" />}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:ignore="VectorDrawableCompat" />

            <androidx.cardview.widget.CardView
                android:id="@+id/cardView"
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_marginStart="20dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:elevation="10dp"
                android:translationZ="10dp"
                app:cardBackgroundColor="@color/movieButtonTextGrey"
                app:cardCornerRadius="10dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/back">

                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:srcCompat="@drawable/heart_selected"
                    tools:ignore="VectorDrawableCompat" />

            </androidx.cardview.widget.CardView>

            <TextView
                android:id="@+id/title"
                style="@style/textBlack36Bold"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="10dp"
                android:text="@={viewModel.entity.title, default=`Movie Title`}"
                app:layout_constraintStart_toEndOf="@+id/cardView"
                app:layout_constraintTop_toTopOf="@+id/cardView" />

            <!-- REST OF THE XML... -->

        </androidx.constraintlayout.widget.ConstraintLayout>

    </ScrollView>
</layout>

In this xml we have defined a variable named viewModel of type MovieTitleViewModel.java

  • Now finally in our View class which is MovieTitleActivity.java

    public class MovieTitleActivity extends BaseActivity {
    ActivityMovieBinding binding;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final MovieTitleViewModel viewModel = new ViewModelProvider(this).get(MovieTitleViewModel.class);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_movie);
        binding.setViewModel(viewModel);
    
        binding.setLifecycleOwner(this);
    
    }
    }

In the first two lines, we initialize our MovieTitleViewModel.java and ActivityMovieBinding.java objects.

Then we set the variable we defined(named viewModel) in out XML file.

Then we set the Lifecycle owner for out ViewBinding, this is important otherwise your xml will not listen to changes made to the MovieEntity pojo object.

  • If you look at our XML file you’ll see how we have bind our data from view model to out widgets in our xml layout.

    • Binding text:

      android:text="@={viewModel.entity.title, default=`Movie Title`}"

    • Binding OnClick events:

      android:onClick="@{()->viewModel.favourite()}"

      This favourite() method is present in out MovieTitleViewModel.java class.


Written by Gagandeep Rangi who likes to talk about himself in third person. Twitter Instagram

Email icon