Android DataBinding provides a way to tie the UI with business logic allowing the UI values to update automatically without manual intervention. This reduces lot of boilerplate code in your business logic that you usually write to sync the UI when new data is available. DataBinding is one of the android architecture components suggested by android.

In this article we are going to learn the basics of DataBinding and in the next article, a simple profile screen is built using the RecyclerView with DataBinding.

android-databinding-tutorial-example

1.1 Enabling DataBinding
1.2 Basic Example
1.3 My DataBinding classes are not generated?
1.4 DataBinding in layouts
1.5 Binding Click Listeners / Event Handling
1.6 Updating UI using Observables
1.7 Updating UI using ObservableFields
1.8 Loading Images From URL (Glide or Picasso)
1.9 Binding Java Functions (Imports)

1. DataBinding Basics

1.1 Enabling DataBinding

To get started with DataBinding, you need to enable this feature in your android project first. Open the build.gradle located under app and enable dataBinding under android module. Once enabled, Sync the project and you are good to go.

android {
    dataBinding {
        enabled = true
    }

    compileSdkVersion 27

    defaultConfig {
        applicationId "info.androidhive.databinding"
        minSdkVersion 16
        // ..
    }
}

1.2 Basic Example

Let’s say we want to display user information from a User POJO class. We generally display the info in a TextView using setText() method. But instead of manually calling setText for each user property, DataBinding allows us to bind the values automatically.

The below POJO class creates an User object with name and email.

public class User {
    String name;
    String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

To enable DataBinding in a layout, the root element should start with <layout> tag. Along with it, <data> and <variable> tags are used.

Below is the structure of data-binding layout.

<layout ...>

    <data>
        
        <variable
            name="..."
            type="..." />
    </data>

    <LinearLayout ...>
       <!-- YOUR LAYOUT HERE -->
    </LinearLayout>
</layout>
  • The layout should have <layout> as root element. Inside <layout> the usual code of layout will be placed.
  • A <data> tag follows the <layout>. All the binding variables and methods should go inside <data> tag.
  • Inside <data> tags, a variable will be declared using <variable> tag. The variable tag takes two attributes `name` and `type`. name attribute will be alias name and type should be of object model class. In our case, path to User class.
  • To bind a value, @ annotation should be used. In the below layout, user name and email are bound to TextView using @{user.name} and @{user.email}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        
        <variable
            name="user"
            type="info.androidhive.databinding.User" />
    </data>

    <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/fab_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}" />

    </LinearLayout>
</layout>
  • Once data binding is integrated in layout file, goto Build -> Clean Project and Build -> Rebuild Project. This will generate necessary binding classes.
  • The generated binding classes follows the naming convention considering the layout file name in which binding is enabled. For the layout activity_main.xml, the generated binding class will be ActivityMainBinding (Binding suffix will be added at the end).
  • To bind the data in UI, you need to inflate the binding layout first using the generated binding classes. Below, ActivityMainBinding inflates the layout first and binding.setUser() binds the User object to layout.

You can notice here, we haven’t used findViewById() anywhere.

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import info.androidhive.databinding.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    private User user;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // setContentView(R.layout.activity_main);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        user = new User();
        user.setName("Ravi Tamada");
        user.setEmail("ravi@androidhive.info");

        binding.setUser(user);
    }
}

If you run the app, you can see the user details displayed in TextViews.

android-data-binding-example

1.3 My DataBinding classes are not generated?

Current version of Android Studio fails to generate the binding classes most of the times. Usually this can be resolved by Cleaning & Rebuilding the project. If the problem still persists, goto File ⇒ Invalidate Caches & Restart. This should possibly solve the problem if your layout files are not having any errors.

1.4 DataBinding in <include> layouts

As we can use, we haven’t included CoordinatorLayout, AppBarLayout and other elements in the above example. Usually, we separate the main layout and content in two different layouts i.e activity_main.xml and content_main.xml. The content_main will be included in main layout using <include> tag. Now we’ll see how to enable data binding when we have include layouts.

Below, we have activity_main.xml with CoordinatorLayout, AppBarLayout and FAB.

  • The <layout> tag is used in activity_main.xml layout to enable data binding. As well, the <data> and <variable> tags are used to bind the User object.
  • To pass the user to included content_main layout, bind:user=”@{user}” is used. Without this, the user object won’t be accessible in content_main layout.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="info.androidhive.databinding.User" />
    </data>

    <android.support.design.widget.CoordinatorLayout 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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.AppBarLayout>

        <include
            android:id="@+id/content"
            layout="@layout/content_main"
            bind:user="@{user}" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:srcCompat="@android:drawable/ic_dialog_email" />

    </android.support.design.widget.CoordinatorLayout>
</layout>
  • The content_main.xml again includes <layout> tag to enable the data binding. The <layout>, <data> and <variable> tags are necessary in both parent and included layouts.
  • android:text=”@{user.name}” and android:text=”@{user.email}” attributes are used to display the data in TextViews.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="info.androidhive.databinding.User" />
    </data>

    <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/fab_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}" />

    </LinearLayout>
</layout>
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        setSupportActionBar(binding.toolbar);

        User user = new User();
        user.setName("Ravi Tamada");
        user.setEmail("ravi@androidhive.info");

        binding.setUser(user);
    }
}

Now, if you run the app you can see the data displayed in included layout.

android-data-binding-include-example

1.5 Binding Click Listeners / Event Handling

Not just the data, we can also bind the click and other events on UI elements. To bind a click event, you need to create a class with necessary callback methods.

Below we have a class that handles the FAB click event.

public class MyClickHandlers {

        public void onFabClicked(View view) {
            Toast.makeText(getApplicationContext(), "FAB clicked!", Toast.LENGTH_SHORT).show();
        }
}

To bind the event, we again use the same <variable> tag with the path to handler class. Below android:onClick=”@{handlers::onFabClicked}” binds the FAB click to onFabClicked() method.

<layout xmlns:bind="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="handlers"
            type="info.androidhive.databinding.MainActivity.MyClickHandlers" />
    </data>

    <android.support.design.widget.CoordinatorLayout ...>

        <android.support.design.widget.FloatingActionButton
            ...
            android:onClick="@{handlers::onFabClicked}" />

    </android.support.design.widget.CoordinatorLayout>
</layout>
  • To assign long press event, the method should return boolean type instead of void. public boolean onButtonLongPressed() handles the view long press.
  • You can also pass params while binding. public void onButtonClickWithParam(View view, User user) receives the user object bind from UI layout. In the layout, the parameter can be passed using android:onClick=”@{(v) -> handlers.onButtonClickWithParam(v, user)}”
  • To bind the events, binding.setHandlers(handlers) is called from the activity.

Below example shows different button click events.

package info.androidhive.databinding;

import android.content.Context;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import info.androidhive.databinding.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    private User user;
    private MyClickHandlers handlers;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        setSupportActionBar(binding.toolbar);

        user = new User();
        user.setName("Ravi Tamada");
        user.setEmail("ravi@androidhive.info");

        binding.setUser(user);

        handlers = new MyClickHandlers(this);
        binding.content.setHandlers(handlers);
    }

    public class MyClickHandlers {

        Context context;

        public MyClickHandlers(Context context) {
            this.context = context;
        }

        public void onFabClicked(View view) {
            Toast.makeText(getApplicationContext(), "FAB clicked!", Toast.LENGTH_SHORT).show();
        }

        public void onButtonClick(View view) {
            Toast.makeText(getApplicationContext(), "Button clicked!", Toast.LENGTH_SHORT).show();
        }

        public void onButtonClickWithParam(View view, User user) {
            Toast.makeText(getApplicationContext(), "Button clicked! Name: " + user.name, Toast.LENGTH_SHORT).show();
        }

        public boolean onButtonLongPressed(View view) {
            Toast.makeText(getApplicationContext(), "Button long pressed!", Toast.LENGTH_SHORT).show();
            return false;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="info.androidhive.databinding.User" />

        <variable
            name="handlers"
            type="info.androidhive.databinding.MainActivity.MyClickHandlers" />
    </data>

    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        ...>

        <android.support.design.widget.AppBarLayout
            ...>

        </android.support.design.widget.AppBarLayout>

        <include
            android:id="@+id/content"
            layout="@layout/content_main"
            bind:user="@{user}" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            android:onClick="@{handlers::onFabClicked}"
            app:srcCompat="@android:drawable/ic_dialog_email" />

    </android.support.design.widget.CoordinatorLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="info.androidhive.databinding.User" />

        <variable
            name="handlers"
            type="info.androidhive.databinding.MainActivity.MyClickHandlers" />
    </data>

    <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/fab_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.email}" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{handlers::onButtonClick}"
            android:text="CLICK" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{(v) -> handlers.onButtonClickWithParam(v, user)}"
            android:text="CLICK WITH PARAM" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="LONG PRESS"
            android:onLongClick="@{handlers::onButtonLongPressed}" />

    </LinearLayout>
</layout>
android-data-binding-button-click-events

1.6 Updating UI using Observables

Observables provides way to automatically sync the UI with data without explicitly calling setter methods. The UI will be updated when the value of a property changes in an object. To make the object observable, extend the class from BaseObservable.

  • To make a property observable, use @Bindable annotation on getter method.
  • Call notifyPropertyChanged(BR.property) in setter method to update the UI whenever the data is changed.
  • The BR class will be generated automatically when data binding is enabled.

Below is the modified User class the extends BaseObservable. You can notice here notifyPropertyChanged is called after assigning new values.

package info.androidhive.databinding;

import android.databinding.BaseObservable;
import android.databinding.Bindable;

public class User extends BaseObservable {
    String name;
    String email;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
        notifyPropertyChanged(BR.email);
    }
}

To test this, I am changing user data on FAB click. You can see the UI updated on FAB click right away.

    public class MyClickHandlers {

        Context context;

        public MyClickHandlers(Context context) {
            this.context = context;
        }

        public void onFabClicked(View view) {
            user.setName("Ravi");
            user.setEmail("ravi8x@gmail.com");
        }
    }

1.7 Updating UI using ObservableFields

If your object class has fewer properties to be updated or if you don’t want to observe every field in the object, you can use ObservableFields to update the UI. You can declare the variable as ObservableField and when the new data is set, the UI will be updated.

The same User class can be modified as below using ObservableFields.

package info.androidhive.databinding;

import android.databinding.ObservableField;

public class User {
    public static ObservableField<String> name = new ObservableField<>();
    public static ObservableField<String> email = new ObservableField<>();

    public ObservableField<String> getName() {
        return name;
    }

    public ObservableField<String> getEmail() {
        return email;
    }
}

To update the values, you need to assign new value to property directly instead of using setter method.

    public class MyClickHandlers {

        Context context;

        public MyClickHandlers(Context context) {
            this.context = context;
        }

        public void onFabClicked(View view) {
            user.name.set("Ravi");
            user.email.set("ravi8x@gmail.com");
        }
    }

1.8 Loading Images From URL (Glide or Picasso)

You can also bind an ImageView to an URL to load the image. To bind an URL to ImageView, you can use @BindingAdapter annotation to object property.

Below, profileImage variable is bound to android:profileImage attribute. The image will be loaded using Glide or Picasso image library.

package info.androidhive.databinding;

import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.databinding.BindingAdapter;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;

public class User {
    //..
    String profileImage;

    public String getProfileImage() {
        return profileImage;
    }

    public void setProfileImage(String profileImage) {
        this.profileImage = profileImage;
    }

    @BindingAdapter({"android:profileImage"})
    public static void loadImage(ImageView view, String imageUrl) {
        Glide.with(view.getContext())
                .load(imageUrl)
                .into(view);

        // If you consider Picasso, follow the below
        // Picasso.with(view.getContext()).load(imageUrl).placeholder(R.drawable.placeholder).into(view);
    }
}

To load the image into ImageView, add the android:profileImage=”@{user.profileImage}” attribute.

<ImageView
     android:layout_width="100dp"
     android:layout_height="100dp"
     android:layout_marginTop="@dimen/fab_margin"
     android:profileImage="@{user.profileImage}" />

Make sure you have added INTERNET permission in your manifest file.

<uses-permission android:name="android.permission.INTERNET" />
android-data-binding-loading-image-from-url

1.9 Binding Java Functions (Import)

You can also bind java functions to UI elements. Let’s say you want to perform some operation on the value before displaying on the UI, you can easily do that using <import> tag.

Here we have a method that transform string to all capital letters.

public class BindingUtils {
    public static String capitalize(String text) {
        return text.toUpperCase();
    }
}

To call this function in your layout, import the class first using <import> tag and call the function on the attribute.

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

    <data>
        <import type="info.androidhive.databinding.BindingUtils" />
    </data>

    <LinearLayout ...>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{BindingUtils.capitalize(user.name)}" />

    </LinearLayout>
</layout>

2. DataBinding in RecyclerView

Now that we have basics of DataBinding, what about DataBinding in RecyclerView adapter? I have created a sample app that explains basic data binding along with RecyclerView list example.

Read: Android DataBinding in RecyclerView – Profile Screen

android-data-binding-in-recyclerview-profile-screen

References

For more information on DataBinding, read https://developer.android.com/topic/libraries/data-binding/index.html

Hi there! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If you have any idea that you would want me to develop? Let’s talk: ravi@androidhive.info
  • Vishal

    Hi Sir,
    I am having a problem after inserting dataBinding in android gradle, i am keep facing an error unable to dex,
    i have also looked on some stackoverflow post but it also didn’t help. I have tried all sorts of things like enabling multidex, cleaning and building the project repeatedly.If it helps i am using android studio 3.0.1.
    Any suggestions?

  • Soft key

    Is there any special reason to make

    numberOfFollowers as ObservableField
    whereas name is also being changed while chaging the value in the model class?

    • Hi

      Is there any special reason to make numberOfFollowers as ObservableField whereas name is also being changed while changing the value in the model class?
      This example is just to demonstrate different ways to update the UI. If all the fields are changing, you can consider @bindable annotation to make it easier. I would suggest consider only one way in a model.

      MyClickHandlers has been taken to handle the click listeners is there any special reason? Could we not just implement the OnClickListener in activity and override the onClick method?
      We can do that, but as we are considering the dataBinding, this is the way to do it. If you consider normal way, you need to import the element first using findById() and then override the onClick method. Again in onClick method, you need to use switch case to identify the element if there are multiple click listeners which will increase the code. In dataBinding, all this can be done with fewer lines of code.

      • Soft key

        Many thanks

        • Cheers!

          • Soft key

            is there any way to get context in BindingUtils. Let’s say I have a TextView that will only be visible if and only if user is logged in or the value of the TextView depends on the login status. now I need to check my SharedPreferences for that I need context. So is there any process to get context in BindingUtils or any how can we access context in xml file?? You explained a way to get the view that is view.getContext() in the model but I dont want to do it in the model. Is there any process??

  • Soft key

    Hello I am facing a problem. I am using ROOM ORM where my user model is user table and annoted by @Entity ,the same model can not be used for data binding . Getting errors like

    cannot find symbol class BR
    package com.packagename.databinding does not exist error: cannot find symbol class MainActivityBinding

    Not getting any helpful solution. please help

  • Jeady Chatrola

    i have checked your databinding github project,and you have used Post class as recycleview pojo,in which you have not extand BaseObservable ,nor used observabeField. then how recycleview ui will be update?

    in short , how i can notify adapter if there is any data change?

    sorry for bad english.

  • Hello i have glide related problem, your code in tutorial working fine but i am not able to load image with GlideApp
    Here is my code
    GlideApp
    .with(view.getContext())
    .load(imageUrl)
    .thumbnail(0.1f)
    .placeholder(R.drawable.esaraswati_logo)
    .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
    .listener(new RequestListener() {

    @Override
    public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
    // holder.mProgressBar.setVisibility(View.VISIBLE);
    return false;
    }

    @Override
    public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
    // holder.mProgressBar.setVisibility(View.GONE);
    return false;
    }
    });

  • karthik A

    This is good one.Thanks Ravi.

  • Soft key

    Hi Ravi ,
    I am facing a problem please suggest me a solution.
    I am building an app where two type of user can login. The design of the login pages are same. I need to maintain two different java class for two type of users like User1LoginActivity.java and User2LoginActivity.Java. As both of the login screen designs are same I want to use the same layout. Then I can not use the click handlers for two different java classes because type is not matching. Please give me good suggestion.
    Thanks in advance.

  • Anmol Kumar

    Hi Ravi,

    i need a bit of assistance..

    I am building a android based app, to make it sync data from when online, and store on device and use this stored data whenever the app goes to offline mode..

    do you have any examples?

    appreciate your assistance on this please..