Using Data Binding API in RecyclerView

With Google IO 2015 came with a great deal of delights for developers, one of them is the Data Binding library. This library allows us to eliminate a ton of boilerplate code such as findByViewId() calls, adding references to views (inside activities/fragments), setting listeners, etc. This support library allows us to declare custom attributes on the fly (and associate it with static methods), instead of declaring it in attrs.xml. It also supports ternary and null coalescing operators, call object methods and integration with collections framework.

In this article we will look into how to use Data Binding with RecyclerView. So lets go ahead and see how it all works.

Initialization

  1. Make sure you are using Android Studio 1.3.
  2. Add following dependencies to your Application’s build.gradle.

  3. dependencies {
          classpath "com.android.tools.build:gradle:1.3.0-beta3"
          classpath "com.android.databinding:dataBinder:1.0-rc0"
       }
    }

  4. Add databinding plugin after android plugin, and make sure jcenter is in the repositories list of your sub projects.

  5. apply plugin: 'com.android.application'

    apply plugin: 'com.android.databinding'

Implement RecyclerView using Data Binding API

  1. While implementing Recyclerview with data binding, we need to modify the recycler listview’s item layout to the one mentioned below.

  2. <layout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto">
       <data>
       </data>

       <!-- Add Your Existing layout here-->

    </layout>

  3. The <data> tag lets us add domain model information to the xml file. It’s similar to declaring a variable of a certain type.

  4.    variable name="book"

             type="com.mutualmobile.example.databinding.model.Book" />

    We can also use the <import> tag to import the class and set class type to the variable directly. In this example, we have a Book class which we import using the <import> tag and set the <variable> tag with type Book.


       <import type="com.mutualmobile.example.databinding.model.Book" />

       <variable name="book" type="Book" />

    Now we can set the data in the object to any part of the UI. In this case we are setting the book’s title to the TextView below.


       <TextView

          android:id="@+id/book_title_textview"

          android:layout_width="match_parent"

          android:layout_height="wrap_content"

          android:text="@{book.title}"

          tools:text="Title" />

  5. Now let’s apply this to implementing an item view layout of a RecyclerView.

  6. <?xml version="1.0" encoding="utf-8"?>

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

       xmlns:app="http://schemas.android.com/apk/res-auto">

       <data>

          <import type="com.mutualmobile.example.databinding.model.Book" />

          <variable

             name="book"

             type="Book" />

          </data>

          <LinearLayout xmlns:tools="http://schemas.android.com/tools"

             android:layout_width="match_parent"

             android:layout_height="wrap_content"

             android:orientation="horizontal">

             <ImageView

                android:id="@+id/book_thumbnail_imageview"

                android:layout_width="80dp"

                android:layout_height="100dp"

                app:imageUrl="@{book.thumbnail}"

                tools:src="@android:drawable/sym_def_app_icon" />

             <LinearLayout

                android:layout_width="match_parent"

                android:layout_height="wrap_content"

                android:layout_gravity="center_vertical"

                android:orientation="vertical">

                <TextView

                   android:id="@+id/book_title_textview"

                   android:layout_width="match_parent"

                   android:layout_height="wrap_content"

                   android:text="@{book.title}"

                   tools:text="Title" />

                <TextView

                   android:id="@+id/book_authors_textview"

                   android:layout_width="match_parent"

                   android:layout_height="wrap_content"

                   android:text="@{book.author}"

                   tools:text="Author name" />

                <TextView

                   android:id="@+id/book_publisher_textview"

                   android:layout_width="match_parent"

                   android:layout_height="wrap_content"

                   android:text="@{book.publisher}"

                   tools:text="Publisher name" />

          </LinearLayout>

       </LinearLayout>

    </layout>

  7. Now we’ll need to use DataBindingUtil to create a ViewDataBinding which we can then use to bind data to the UI.

  8. public static class BindingHolder extends RecyclerView.ViewHolder {

          private ViewDataBinding binding;

          public BindingHolder(View rowView) {

             super(rowView);

             binding = DataBindingUtil.bind(rowView);

          }

          public ViewDataBinding getBinding() {

             return binding;

          }

       }

    Without Data Binding:

    public static class ViewHolder extends RecyclerView.ViewHolder {

          private TextView title, author, publisher;

          private ImageView thumbnail;

          public ViewHolder(View v) {

             super(v);

             title = (TextView) v.findViewById(R.id.book_title_textview);

             author = (TextView) v.findViewById(R.id.book_authors_textview);

             publisher = (TextView) v.findViewById(R.id.book_publisher_textview);

             thumbnail = (ImageView) v.findViewById(R.id.book_thumbnail_imageview);

          }

       }

  9. When we created the layout,  we declared a variable called book which is represented by BR.book. This is a class that is generated automatically by the data binding plugin. To set it to the view, we just need to call the setVariable()on the ViewDataBinding instance we got in the previous step. Then we’ll use executePendingBindings to make binding happen immediately.

  10. @Override

       public void onBindViewHolder(BindingHolder holder, int position) {

          final Book book = mBooks.get(position);

          holder.getBinding().setVariable(BR.book, book);

          holder.getBinding().executePendingBindings();

       }

    Without Data Binding:

    @Override

       public void onBindViewHolder(ViewHolder holder, int position) {

          final Book book = mBooks.get(position);

          holder.author.setText(book.author);

          holder.title.setText(book.title);

          holder.publisher.setText(book.publisher);

          Picasso.with(holder.thumbnail.getContext())

             .load(book.thumbnail)

             .into(holder.thumbnail);

       }

    Note: Sometimes the BR class is not generated automatically. When this happens, try performing a clean rebuild and if the problem still persists, try restarting Android Studio. Hopefully this issue will be fixed before Android Studio 1.3 release in the stable channel.

  11. We declared app:imageUrl in the layout and we passed the book image URL to it. Now we need to load the image URL, which can be done using the @BindingAdapter annotation. To do that,  we need to declare a static method annotated with @BindingAdapter and provide it with a parameter that corresponds to the custom attribute that we declared in the layout, i.e., imageURL. This method will be called as soon as the binding occurs.

  12. public class CustomBindingAdapter {

       @BindingAdapter("bind:imageUrl")

       public static void loadImage(ImageView imageView, String url) {

          Picasso.with(imageView.getContext()).load(url).into(imageView);

       }

    }

    And that’s it. Now the RecyclerView will display the data using the Data Binding library.

    Here is the complete Adapter for reference. Notice how much boilerplate code has been eliminated.


    public class BooksRecyclerAdapter extends RecyclerView.Adapter<BooksRecyclerAdapter.BindingHolder> {

       private List<Book> mBooks;

       public static class BindingHolder extends RecyclerView.ViewHolder {

          private ViewDataBinding binding;

          public BindingHolder(View rowView) {

             super(rowView);

             binding = DataBindingUtil.bind(rowView);

          }

          public ViewDataBinding getBinding() {

             return binding;

          }

       }

       public BooksRecyclerAdapter(List<Book> recyclerUsers) {

          this.mBooks = recyclerUsers;

       }

       @Override

       public BindingHolder onCreateViewHolder(ViewGroup parent, int type) {

          View v = LayoutInflater.from(parent.getContext())

                .inflate(R.layout.row_search_result, parent, false);

          BindingHolder holder = new BindingHolder(v);

          return holder;

       }

       @Override

       public void onBindViewHolder(BindingHolder holder, int position) {

          final Book book = mBooks.get(position);

          holder.getBinding().setVariable(BR.book, book);

       holder.getBinding().executePendingBindings();

       }

       @Override

       public int getItemCount() {

          return mBooks.size();

       }

    }

    A working sample of this is available on Github.

Stay-Up-To-Date

Keep in the loop with the latest in emerging technology and Mutual Mobile