DEV Community

Subbu Lakshmanan
Subbu Lakshmanan

Posted on • Updated on

Android Data Binding

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
  1. What is BR in itemBinding.setVariable(BR.dataBindingItem, data);
- BR is BindingResource that is generated by Gradle as similar to *R* classes in android.
  1. 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)

Collapse
 
manishkherde profile image
manishkherde

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

Collapse
 
subbramanil profile image
Subbu Lakshmanan • Edited

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),

  1. Source of Data-binding library should present in here. It has a sample app that might give some idea.
  2. The prebuilt aar file in here.

Please verify if you have either one. In my opinion, using the pre-built aar file would be simple to start with.

Collapse
 
zhjian1987 profile image
zhjian1987

Hi,Subbu

As we know if compile app with android studio to support data-binding, we only need to add

dataBinding {
    enabled true
}

in build.gralde.

But how to enable android data-binding in Android.mk if we want to compile app with AOSP? Thanks.

Thread Thread
 
subbramanil profile image
Subbu Lakshmanan • Edited

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,

classpath "com.android.databinding:dataBinder:${config.version}”

In App level gradle file,

apply plugin: ‘com.android.databinding'

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.

com.android.databinding:adapters
com.android.databinding:baseLibrary
com.android.databinding:library

This is what we did in one of our apps to have appcompat support.

LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat \
                                                               android-support-v4

LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml
LOCAL_SRC_FILES := $(call all-java-files-under, app)
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/app/src/main/res \
                                                   frameworks/support/v7/appcompat/res

LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.design \
--extra-packages android.support.v7.appcompat

Similar to this there should be a way to add the DataBinding libraries as well.

Sorry It took long to respond.

Thread Thread
 
sankes profile image
shankes

HI Subbu,could you give me a demo about how to support databinding in AOSP.
Thanks very much

Collapse
 
juliushamdali profile image
juliushamdali

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?

Collapse
 
krishnagolakoti profile image
KrishnaGolakoti

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.

Collapse
 
riatauro profile image
riatauro • Edited

Hi KrishnaGolakoti Were you able to solve this..? i'm getting the same error

Collapse
 
subbramanil profile image
Subbu Lakshmanan

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.

Collapse
 
sankes profile image
shankes

Hi juliushamdali:
Could you let me know how to support databinding in AOSP?
Thanks very much.