Subbu Lakshmanan

Android Data Binding

Notes from learning Android Data binding from Pluralsight course as well from other resources

Android Data Binding

In this series of blogs, I am going to put down my notes from learning the course 'Android Fundamentals: Data binding' from Pluralsight and other resources from web.

Disclaimer: These are my notes from the 'PluralSight' course and I do not mean to reproduce something copyrighted.

Traditional Approach

How many of the android developers are tired of writing the code as below,

View view = inflater.inflate(R.layout.network_config_layout, parent, false);
EditText ipEditTxtView = (EditText) view.findViewById(R.id.nw_config_ip_address_value);
ipEditTxtView.setText(nwConfig.getIpAddress());

Button saveNWConfigBtn = (Button) view.findViewById(R.id.save_nw_config);
saveNWConfigBtn.setOnClickListener(this);

Basically, the steps of traditional view binding are,

  1. Inflate the XML
  2. Find the required element from the XML
  3. Assign it to a local/member variable
  4. Get the value from data
  5. Assign the value or Assign the event listener

There is nothing wrong in the above data binding process, however one gets tired of writing this code over a period of time.

If anyone is curious, there is a famous live template in Android Studio that can write the line to find the element from view.

  1. Enter fbc
  2. Press Command + Space(Mac) or Ctrl + Space(Windows)
  3. Android Studio will auto complete the line as () findViewById(R.id.);

View Binding Libraries

The author provides a bunch of View binding library alternatives.

  1. RoboBinding
  2. ButterKnife
  3. AndroidAnnotations

I have used ButterKnife before and I found it pretty easy to use. I do not want to get into implemenation details of these libraries since the intention of this blog is about Google's Android Data Binding library.

Data Binding Library Process

The steps involved here are,

  1. Creating a binding class from Layout
  2. Retrieve the data
  3. Bind the data to the view

The all mighty gradle plugin helps with the process of creating the binding class from the view.

  1. Analyze the layout file
  2. Creates the binding class file
  3. Create binding methods in class file

Note: Use the latest version of Android Studio and make sure to download the Android Support Repository from SDK Manager.
As per the time of writing this blog, I was using Android Studio 2.3.1 released on April 1, 2017 and have downloaded all support libraries.

I re-used one of my old projects from Android Nano Degree for this course. The app was a simple weather app that shows the weather forecast for a week for a given location and temperature Unit. I used recycler view to show the forecast for a week, AsyncTask to make the service request to OpenWeatherMap API and Picasso image loading library to load weather icons.

You can find a screenshot here: App Screenshot

Useful Link: Android Data Binding

Google simplified integrating the data binding support to the Android apps. In the app level build.gradle file, I added a block to enable data binding.

android {
    ...
    ...
    dataBinding {
            enabled = true
    }
}

Now the app was ready to have some 'Data Binding'. Next I updated the layout file for weather item in the recycler view as follows.

  1. Enclosed the existing layout with <layout> and </layout> tags.
  2. Created a <data> element with one <variable> to hold the Weather data for the row.
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="dataBindingItem"
            type="com.udacity.learning.mysunshineapp.model.WeatherData" />
    </data>

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/list_item_card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/margin_5dp"
        android:background="@color/cardview_background"
        android:padding="@dimen/_10dp">

        <TextView
            android:id="@+id/list_item_date_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:minHeight="?android:attr/listPreferredItemHeight"
            android:text="@{dataBindingItem.dt}"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.10"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.1" />

        <ImageView
            android:id="@+id/list_item_weather_imageview"
            android:layout_width="100dp"
            android:layout_height="100dp"
            app:imageUrl="@{dataBindingItem.weatherIconURL}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.10"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="@id/list_item_date_textview"
            app:layout_constraintVertical_bias="0.90" />

        <TextView
            android:id="@+id/list_item_weather_desc_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{dataBindingItem.weather.get(0).description}"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.40"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/list_item_date_textview"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.80" />

        <TextView
            android:id="@+id/list_item_max_temp_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{dataBindingItem.temp.max}"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.80"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.40" />

        <TextView
            android:id="@+id/list_item_min_temp_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{dataBindingItem.temp.min}"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.80"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/list_item_max_temp_textview"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.80" />

    </android.support.constraint.ConstraintLayout>
</layout>

The name propoerty of the variable is used to refer the data as android:text="@{dataBindingItem.dt}". The data binding library looks for,

  1. A public method with name 'getDt()' that returns a String
  2. A public method with name 'dt()' that returns a String
  3. A public member variable with name 'dt' of type String

to associate the data with the text field.

The type refers to the POJO Data Model Class WeatherData as below.

public class WeatherData extends BaseObservable implements Parcelable {

    @Bindable
    private String dt;
    @Bindable
    private String pressure;
    @Bindable
    private String humidity;
    @Bindable
    private String speed;
    @Bindable
    private String clouds;
    @Bindable
    private TemperatureData temp;
    @Bindable
    private ArrayList<WeatherDesc> weather;
    @Bindable
    private String weatherIconURL;

    ....
}

Notice that the variables are annotated with @Bindable annotation. And the WeatherData class has public getters and setters for the private variables.

Note: Look at the power of data binding support in the xml files. For example, in this line android:text="@{dataBindingItem.weather.get(0).description}" I was able to access to description of first time of a weather list.

Next step was to update the adapter that I used for the RecyclerView as follows.

  1. Update the ViewHolder associated
class ForecastViewHolder extends RecyclerView.ViewHolder {
    private final ViewDataBinding itemBinding;

    ForecastViewHolder(ViewDataBinding binding) {
        super(binding.getRoot());
        this.itemBinding = binding;
    }

    void bind(WeatherData data) {
        itemBinding.setVariable(BR.dataBindingItem, data);
    }
}
  1. Update the onCreateViewHolder and onBindViewHolder methods of Recycler Adapter
public class ForecastDataAdapter extends RecyclerView.Adapter<ForecastDataAdapter.ForecastViewHolder> {

    ...

    @Override
    public ForecastViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewDataBinding mForecastItemBinding = DataBindingUtil.inflate(inflater, R.layout.list_item_forecast_constraint, parent, false);
        return new ForecastViewHolder(mForecastItemBinding);
    }

    @Override
    public void onBindViewHolder(ForecastViewHolder holder, int position) {
        WeatherData data = weatherData.get(position);
        holder.bind(data);
    }

    ...
}

There are few things to note here:

  1. The ViewDataBinding in private final ViewDataBinding itemBinding;

    • ViewDataBinding is the base class for generated data binding classes
  2. What is BR in itemBinding.setVariable(BR.dataBindingItem, data);

    • BR is BindingResource that is generated by Gradle as similar to R classes in android.
  3. What is DataBindingUtil in ViewDataBinding mForecastItemBinding = DataBindingUtil.inflate(inflater, R.layout.list_item_forecast_constraint, parent, false);

    • DataBindingUtil is a Utility class that data binding support adds to help with inflating the view and create binding class. This line inflates the view and also binds the view.

Note: There are several ways to create a binding class, (Using Activity, Fragments, etc.,).

When I tried to run the app, I got few compilatoin issues about, private TemperatureData temp and private ArrayList<WeatherDesc> weather and it didn't create the BR file. For a while, I didn't understand the error and trying to clean the project. I was hoping that would fix the issue and create the Binding Resource file. However after reading the error properly and reviewed the error file, I realized that the classes, TemperatureData and WeatherDesc also needs to be suitable for Data Binding. I modified the classes as below,

public class TemperatureData extends BaseObservable {

    @Bindable
    private String day;
    @Bindable
    private String night;
    @Bindable
    private String morn;
    @Bindable
    private String eve;
    @Bindable
    private String max;
    @Bindable
    private String min;

    ...
}

After making the above changes, I was able to run the app without any crashes.

I made few changes after to load the images using Picasso following the article from here, that explains way better. I do not want to take credit or duplicate the effort here.

This was a simple implementation and I'm still learning. I hope to write more, probably like a series of blogs on Data binding as I learn adn try different things.

This post is also available on DEV.

All rights reserved