Android Data Binding
Subbu Lakshmanan
Posted on May 13, 2017
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,
- Inflate the XML
- Find the required element from the XML
- Assign it to a local/member variable
- Get the value from data
- 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.
- Enter
fbc
- Press Command + Space(Mac) or Ctrl + Space(Windows)
- Android Studio will auto complete the line as
() findViewById(R.id.);
View Binding Libraries
The author provides a bunch of View binding library alternatives.
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,
- Creating a binding class from Layout
- Retrieve the data
- Bind the data to the view
The all mighty gradle plugin helps with the process of creating the binding class from the view.
- Analyze the layout file
- Creates the binding class file
- 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:
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.
- Enclosed the existing layout with
<layout>
and</layout>
tags. - 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,
- A public method with name 'getDt()' that returns a String
- A public method with name 'dt()' that returns a String
- 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.
- 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);
}
}
- 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:
- The ViewDataBinding in
private final ViewDataBinding itemBinding;
- ViewDataBinding is the base class for generated data binding classes
- What is BR in
itemBinding.setVariable(BR.dataBindingItem, data);
- BR is BindingResource that is generated by Gradle as similar to *R* classes in android.
- 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.
Posted on May 13, 2017
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.