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.
Top comments (10)
Hi Subbu,
I'm working on AOSP internal apps and using AOSP Marshmallow version on Ubuntu 5.x. I wanted to use data binding in these apps. But here is the problem,
Whenever I build my app with databinding integration, throws a error for databinding objects and classes.
Can you help me out to understand how to build (make) databinding apps in AOSP. May be I need to specify some parameters in the make file, but I am not sure what exactly to write in make file.
Please :-)
Thanks in advance
Absolutely I love to help you in whatever way I can. Please post the error that you see when compiling the AOSP module, so that I can get a clear idea.
If you are using Marshmallow version of AOSP (I'm assuming the release version: 6.0.0_r1),
Please verify if you have either one. In my opinion, using the pre-built aar file would be simple to start with.
Hi,Subbu
As we know if compile app with android studio to support data-binding, we only need to add
in build.gralde.
But how to enable android data-binding in Android.mk if we want to compile app with AOSP? Thanks.
Hi,
What's the Android version you are using?
I’m not sure how much I can help as I haven’t done it before.
I believe DataBinding should be enabled from 'build.gradle' file. In Android.mk file, you should declare the dependencies.
Check it out this link:
androidxref.com/6.0.1_r10/xref/fra...
From my observation of this app,
It uses,
In Project level gradle file,
In App level gradle file,
By looking at the module definition of data-binding library,
androidxref.com/6.0.1_r10/xref/fra...
And something interesting I found from this SO question,
stackoverflow.com/questions/481139...
I would imagine, you might need to reference the libraries in your make file.
This is what we did in one of our apps to have appcompat support.
Similar to this there should be a way to add the DataBinding libraries as well.
Sorry It took long to respond.
HI Subbu,could you give me a demo about how to support databinding in AOSP.
Thanks very much
hi Subbu, been going thru yo article i am playing around with the data binding library , very interesting and it eliminates a lot of boilerplate code. i ve also managed to use ImageBindingAdapter like below to get images from an api that simply take the end url as /fdfdgytedgda.jpg so far so good.
public final class ImageBindingAdapter {
@BindingAdapter(value = "imageUrl")
public static void loadImageUrl(ImageView view, String url) {
if (url != null && !url.equals(""))
Picasso.with(view.getContext())
.load(Constant.IMAGE_ENDPOINT_PREFIX + url)
.placeholder(R.drawable.placeholder)
.into(view);
}
No problem at all with the above, but i have a scenario that seems un-obvious
to me that the requirement is to fetch images by size as small medium or large. the images are only identified with image ID like 34231214 which is saved in sqlite that requires specifying the size as picture_size=large when fetching. how can this be handled the endpoint is say subbramanil.com/api/pictures/232242/picture_size=large .where does the binding of the picture_size=large take place?
Hi Juliushamdali,
Could you please let me know if you were able to use data binding in AOSP? I'm also trying to include this, but using of <data tags, <variable name, type attributes are not getting resolved in xml to generate the code.
Hi KrishnaGolakoti Were you able to solve this..? i'm getting the same error
Hi Buddy,
That's an interesting requirement. Have you checked this answer by Jake Wharton?
Question - How to pass additional context information for a custom Downloader.
The idea is to create a custom URI and pass it to Picasso also configure Picasso to use a custom downloader that knows how to parse the response.
Good luck!! Check it out and let me know how it goes.
P.S: I am in a it tight spot at work, otherwise I would love to help you out.
Hi juliushamdali:
Could you let me know how to support databinding in AOSP?
Thanks very much.