Home Tutorials Training Consulting Products Books Company Donate Contact us









NOW Hiring

Quick links

Share

This tutorial describes the usage of data binding in Android applications. Data binding allows to synchronize your user interface with your application model and logic.

1. Using data binding in Android applications

1.1. Introduction to data binding in Android

Android offers support to write declarative layouts using data binding. This minimizes the necessary code in your application logic to connect to the user interface elements.

The usage of data binding requires changes in your layout files. Such layout files starts with a layout root tag followed by a data element and a view root element. The data elements describe data which is available for binding. This view element contains your root hierarchy similar to layout files which are not used with data binding. References to the data elements or expressions within the layout are written in the attribute properties using the @{} or @={},

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="temp" type="com.vogella.android.databinding.TemperatureData"/> (1)
   </data>
   <LinearLayout (2)
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{temp.location}"/>
       <TextView
               android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@{temp.celsius}"/>
   </LinearLayout>
</layout>
1 The user variable within data describes a property that may be used within this layout.
2 Normal view hierarchy

Android data binding generates a Binding class based on this layout. This class holds all the bindings from the layout properties, i.e., the defined variable to the corresponding views. It also provides generated setters for your data elements from the layout. The name of the generated class is based on the name of the layout file. This name is converted to Pascal case and the Binding suffix is added to it. For example, if the layout file is called activity_main.xml, the generate class is called ActivityMainBinding. You can inflate the layout and connect your model via this class or the DataBindingUtil class.

TemperatureData temperatureData = // your data is created here
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setTemp(temperatureData); // generated setter based on the data in the layout file

You can also inflate the layout, which is useful for late binding, as for example, in a RecyclerView.

ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater(), container, attachToContainer);
// get the root view
View view = binding.getRoot();
// do more stuff
TemperatureData temperatureData = // your data is created here
binding.setTemp(temperatureData); // generated setter based on the data in the layout file

You can also inflate layouts for RecyclerView, ViewPager, or other things that aren’t setting the Activity contents.

1.2. Enable data binding in your Android application

To enable the usage of data binding in your Android application, add the following snippet to the app/build.gradle file.

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

1.3. Data binding for events via listener bindings and method references

Events may be bound to handler methods directly, similar to the way android:onClick can be assigned to a method in the activity. Event attribute names are governed by the name of the listener method with a few exceptions. For example, View.OnLongClickListener has a method onLongClick(), so the attribute for this event is android:onLongClick.

To assign an event to its handler, use a normal binding expression, with the value being the method name to call. The binding expression can assign the click listener for a View.

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

    <data>
        <variable
            name="presenter"
            type="com.vogella.android.databinding.MainActivityPresenter"/>
    </data>


           <Button
            android:text="Start second activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> presenter.showList()}"
            />

</layout>

You could also bind to a method reference via android:onClick="@{handlers::onClickFriend}"/>. If you methods need parameters, you can also pass your data object to them. For example:

android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

1.4. Imports

You can also import classes to use them in your data binding expressions.

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>

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

1.5. Updating the user interfaces with changes from the data model

Any plain old Java object (POJO) can be used for data binding. But if updates in the data model should also update the user interface, the objects must be able to notify about data changes. There are three different data change notification mechanisms: * observable objects * observable fields * observable collections

Android provides the BaseObservable class which you can extend. The data class is responsible for notifying when the properties change. This is done by assigning a @Bindable annotation to the getter and notifying in the setter.

package com.vogella.android.databinding;


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

import java.util.Observable;

public class TemperatureData extends BaseObservable {
    private String celsius;

    public TemperatureData(String celsius) {
        this.celsius = celsius;
    }

    @Bindable                                      (1)
    public String getCelsius() {
        return celsius;
    }

    public void setCelsius(String celsius) {
        this.celsius = celsius;
        notifyPropertyChanged(BR.celsius);         (2)
    }
}
1 Define a relevant getter
2 Notify any listeners, BR.celsius is a generated class

This listener is invoked on every update and it updates the corresponding views. This ensures that updates in the model updates also the UI.

Alternatively to create a observable class, you can also use ObservableField and its subclass for properties.

private class TemperatureData {
   public final ObservableField<String> celsius = new ObservableField<>();
   public final ObservableField<String> location =  new ObservableField<>();
}

To access such fields in your code, use the set and get methods.

temp.location.set("Hamburg");
String celsius  = temp.celsius.get();

1.6. Custom converters

Sometimes you have to perform complex data conversions. For this, you can register a custom convertor via the static @BindingAdapter method. This method can be placed anywhere in your code and can override the default conversion of a field to your data model.

For example, assume that you want to assign a field of your data model to an image view.

  <ImageView
            android:id="@+id/icon"
            android:layout_width="40dp"
            android:layout_height="fill_parent"
            android:layout_alignParentBottom="true"
            android:layout_alignParentTop="true"
            android:layout_marginRight="6dip"
            android:contentDescription="TODO"
            android:src="@{obj.url}"
            />

You can register for this property on ImageView with the following method. This method uses Glide to download the image.

@BindingAdapter("android:src")
    public static void setImageUrl(ImageView view, String url) {
        Glide.with(view.getContext()).load(url).into(view);
    }

2. Exercise: Using data binding in Android applications

In this exercise you learn how to interact between your user interface widgets using data binding. Create a new Android application for this exercise, with the com.vogella.android.databinding top level package.

2.1. Activate the usage of data binding

Open your app/build.gradle file and activate the usage of data binding.

apply plugin: 'com.android.application'

android {

    dataBinding {
        enabled = true
    }

        .... [REST AS BEFORE...]

Ensure you pick the correct build file.

2.2. Create classes for the view interaction

Create the following classes.

package com.vogella.android.databinding;


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

public class TemperatureData extends BaseObservable {
    private String location;
    private String celsius;

    public TemperatureData(String location, String celsius) {
        this.location = location;
        this.celsius = celsius;
    }

    @Bindable
    public String getCelsius() {
        return celsius;
    }

    @Bindable
    public String getLocation() {
        return location;
    }

    public  void setLocation(String location){
        this.location = location;
        notifyPropertyChanged(BR.location);
    }

    public void setCelsius(String celsius) {
        this.celsius = celsius;
        notifyPropertyChanged(BR.celsius);
    }

}

After the definition of the layout file, the missing BR class is generated.

package com.vogella.android.databinding;


public interface MainActivityContract {
    public interface Presenter {
        void onShowData(TemperatureData temperatureData);
    }

    public interface View {
        void showData(TemperatureData temperatureData);
    }

}
package com.vogella.android.databinding;

public class MainActivityPresenter implements MainActivityContract.Presenter {
    private MainActivityContract.View view;

    public MainActivityPresenter(MainActivityContract.View view) {
        this.view = view;
    }

    @Override
    public void onShowData(TemperatureData temperatureData) {
        view.showData(temperatureData);
    }

}

2.3. Adjust layout file and activity to use data binding

Change the layout to the following.

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

    <data>
        <variable
            name="temp"
            type="com.vogella.android.databinding.TemperatureData" />
        <variable
            name="presenter"
            type="com.vogella.android.databinding.MainActivityPresenter"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={temp.location}"
             />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(temp.celsius)}"
            />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="@{String.valueOf(temp.celsius)}" />

        <Button
            android:text="Show data model"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> presenter.onShowData(temp)}"
            android:id="@+id/button" />
        <Button
            android:text="Start second activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> presenter.showList()}"
            />

    </LinearLayout>
</layout>

You see some warning messages in the editor, e.g., because you used hard-codes strings. For this exercise we ignore these warnings.

package com.vogella.android.databinding;

import android.app.Activity;
import android.content.Intent;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.widget.Toast;

import com.vogella.android.databinding.databinding.ActivityMainBinding;

public class MainActivity extends Activity implements MainActivityContract.View {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        MainActivityPresenter mainActivityPresenter = new MainActivityPresenter(this);
        TemperatureData temperatureData = new TemperatureData("Hamburg", "10");
        binding.setTemp(temperatureData);
        binding.setPresenter(mainActivityPresenter);
    }

    @Override
    public void showData(TemperatureData temperatureData) {
        String celsius = temperatureData.getCelsius();
        Toast.makeText(this, celsius, Toast.LENGTH_SHORT).show();
    }

}

Adjust your MainActivity code to activate data binding.

2.4. Convince Android Studio to compile your application

If the BR class is missing, select Build ▸ Clean followed by Build ▸ Make Project.

2.5. Validate your application

Start your application. If you press the button, a small popup should be shown with the correct data.

The first text view should The second TextView should update automatically, if you type in the EditText field.

3. Exercise: Using data binding for RecyclerView

In this exercise you learn how to use data binding for a recylerview. Continue to use the com.vogella.android.databinding package.

3.1. Define a new activity and allow to start it

Create a new activity called SecondActivity. Ensure that you add it to your Android manifest.

Adjust your MVP contract to start the second activity.

package com.vogella.android.databinding;


public interface MainActivityContract {
    public interface Presenter {
        void onShowData(TemperatureData temperatureData);
        void showList();
    }

    public interface View {
        void showData(TemperatureData temperatureData);
        void startSecondActivity();
    }

}

Implement this new behavior to start the second activity.

To start the activity you need also to change your layout file.

3.2. Activate data binding

Open your app/build.gradle file and add the dependency to recylerview.

dependencies {
        // more
        compile "com.android.support:recyclerview-v7:25.1.1"
}

Ensure you pick the correct build file.

3.3. Create icon

Add an ic_listentry icon to your application.

3.4. Implement the Recyclerview with data binding.

Create the following layout called `second_main.xml"

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

    <data>
        <variable
            name="temp"
            type="com.vogella.android.databinding.TemperatureData" />
        <variable
            name="presenter"
            type="com.vogella.android.databinding.MainActivityPresenter"/>
    </data>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />
</layout>

Create the following layout called `rowlayout.xml"

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

    <data>

        <variable
            name="obj"
            type="com.vogella.android.databinding.TemperatureData"
            />
    </data>

    <RelativeLayout
                    android:layout_width="fill_parent"
                    android:layout_height="?android:attr/listPreferredItemHeight"
                    android:padding="6dip"
        >

        <ImageView
            android:id="@+id/icon"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_alignParentBottom="true"
            android:layout_alignParentTop="true"
            android:layout_marginRight="6dip"
            android:contentDescription="TODO"
            android:src="@drawable/ic_listentry"
            />

        <TextView
            android:id="@+id/secondLine"
            android:layout_width="fill_parent"
            android:layout_height="26dip"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_toRightOf="@id/icon"
            android:ellipsize="marquee"
            android:text="Description"
            android:textSize="12sp"
            android:maxLines="1"
            />

        <TextView
            android:id="@+id/firstLine"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_above="@id/secondLine"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:layout_alignWithParentIfMissing="true"
            android:layout_toRightOf="@id/icon"
            android:gravity="center_vertical"
            android:text="Example application"
            android:textSize="16sp"
            />

    </RelativeLayout>

</layout>

Create the following class:

package com.vogella.android.databinding;

import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import com.vogella.android.databinding.databinding.RowlayoutBinding;

import java.util.List;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        private List<TemperatureData> data;

        // Provide a reference to the views for each data item
        // Complex data items may need more than one view per item, and
        // you provide access to all the views for a data item in a view holder
        public class MyViewHolder extends RecyclerView.ViewHolder {
                // each data item is just a string in this case
                private final ViewDataBinding binding;

                public MyViewHolder(ViewDataBinding binding) {
                        super(binding.getRoot());
                        this.binding = binding;
                }
                public void bind(Object obj) {
                       binding.setVariable(BR.obj,obj);
                       binding.executePendingBindings();
                }
        }

        public void add(int position, TemperatureData item) {
                data.add(position, item);
                notifyItemInserted(position);
        }

        public void remove(TemperatureData item) {
                int position = data.indexOf(item);
                data.remove(position);
                notifyItemRemoved(position);
        }

        // Provide a suitable constructor (depends on the kind of dataset)
        public MyAdapter(List<TemperatureData> myDataset) {
                data = myDataset;
        }

        // Create new views (invoked by the layout manager)
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                // create a new view
                LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
                RowlayoutBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.rowlayout, parent, false);
                // set the view's size, margins, paddings and layout parameters
                return new MyViewHolder(binding);
        }

        // Replace the contents of a view (invoked by the layout manager)
        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
                final TemperatureData temperatureData = data.get(position);
                holder.bind(temperatureData);

        }

        // Return the size of your dataset (invoked by the layout manager)
        @Override
        public int getItemCount() {
                return data.size();
        }

}
package com.vogella.android.databinding;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.Arrays;
import java.util.List;

public class SecondActivity extends Activity  {

    private RecyclerView recyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second_main);
        recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(mLayoutManager);

        List<TemperatureData> items =
                Arrays.asList(new TemperatureData("Hamburg", "5"), new TemperatureData("Berlin", "6"));

        // define an adapter
        mAdapter = new MyAdapter(items);
        recyclerView.setAdapter(mAdapter);
    }



}

3.5. Validate your application

Start your application and navigate to your second activity.

Ensure the list is correctly displayed.

3.6. Extra: Create an abstract class for your adapter

Most of code in adapter can be the same if data binding is used. The only requirement is that the object name in the layout file is the same, so that the generated entry in the BR class is the same. In our example we use obj for it.

To reuse most of our adapter, create an abstract class with the logic to bind to any object.

package com.vogella.android.databinding;

import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import com.vogella.android.databinding.databinding.RowlayoutBinding;

public abstract class MyBaseAdapter extends RecyclerView.Adapter<MyBaseAdapter.MyViewHolder> {

        // Provide a reference to the views for each data item
        // Complex data items may need more than one view per item, and
        // you provide access to all the views for a data item in a view holder
        public class MyViewHolder extends RecyclerView.ViewHolder {
                // each data item is just a string in this case
                private final ViewDataBinding binding;

                public MyViewHolder(ViewDataBinding binding) {
                        super(binding.getRoot());
                        this.binding = binding;
                }
                public void bind(Object obj) {
                       binding.setVariable(BR.obj,obj);
                       binding.executePendingBindings();
                }
        }

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                // create a new view
                LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
                RowlayoutBinding binding = DataBindingUtil.inflate(layoutInflater, getLayoutIdForType(viewType), parent, false);
                // set the view's size, margins, paddings and layout parameters
                return new MyViewHolder(binding);
        }

        // Replace the contents of a view (invoked by the layout manager)
        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
                holder.bind(getDataAtPosition(position));
        }

        public abstract Object getDataAtPosition(int position);

        public abstract int getLayoutIdForType(int viewType);

}

Now you can adjust your existing adapter to extend the MyBaseAdapter. This is left as exercise for the reader.

Having this base adapter allows you to reuse lots of code for new adapter implementations.

3.7. Extra: Add image property to your data model and show it

Add a field url to your data model and use Glide to download such an image.

Examples URLs for images can be found on http://lorempixel.com/.

@BindingAdapter("android:src")
    public static void setImageUrl(ImageView view, String url) {
        Glide.with(view.getContext()).load(url).into(view);
    }

4. About this website

5. Android databinding resources

Video introduction into Data Binding === vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.