Architecture Components

Android Data Binding RecyclerView

Introduction

In this android data binding RecyclerView tutorial, I’m going to show how to implement data binding in RecyclerView Android. Data binding binds the UI with data sources and reduced line of code. In this android data binding RecyclerView tutorial, we take an entity is an employee. That means is we fetch the employee data from remote and display on RecyclerView.

What is RecyclerView

RecyclerView is self-explanatory, It widget is a more advanced and flexible version of ListView. For more detail read out this blog post: RecyclerView in Android

What is Data binding

Data Binding is part of android architecture components. It allows us to bind the UI components to data sources in a declaration format.

For more information on DataBinding, check out this blog post: Data Binding in Android Tutorial

Android Data Binding RecyclerView Example (Sample App)

Example App

All theoretical part is done. Now come to real implementation. I’m going to example app that contains Retrofit, LiveData, and DataBinding with RecyclerView. After following all steps the final outcome is the same as video. How to do that let’s get started

1. Add dependencies

Let move to android studio and open module app build.gradle and add below dependencies. Make sure you have added latest version of dependencies

dependencies {
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.appcompat:appcompat:1.0.2'
  // constraint layout
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

  // lifecycle
  implementation "android.arch.lifecycle:extensions:1.1.1"
  annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

  // glide for image
  implementation "com.github.bumptech.glide:glide:4.9.0"
  annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"

  // retrofit
  implementation "com.squareup.retrofit2:retrofit:2.6.0"
  implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

  implementation 'androidx.recyclerview:recyclerview:1.0.0'
  implementation 'androidx.cardview:cardview:1.0.0'

  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'androidx.test:runner:1.2.0'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

2. Enable Data Binding

You know we are going to data binding in app tutorials. Just add below line inside android tag

  dataBinding {
    enabled = true
  }

3. Designing MainActivity Layout

Open the activity_main.xml and add following code. This layout contains RecyclerView inside the ConstraintLayout. Which will be displayed no list item. Important things are all layout stuff inside the <layout> tag. Which mean it supports data binding

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    >
  <androidx.constraintlayout.widget.ConstraintLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      tools:context=".MainActivity"
      tools:showIn="@layout/activity_main">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/viewEmployees"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:padding="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

  </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4. Prepare Model

Let’s suppose you want to display the employee’s list using RecyclerView. The JSON contract is below

{
  "page": 1,
  "per_page": 3,
  "total": 12,
  "total_pages": 4,
  "data": [
    {
      "id": 1,
      "email": "george.bluth@reqres.in",
      "first_name": "George",
      "last_name": "Bluth",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
    },
    {
      "id": 2,
      "email": "janet.weaver@reqres.in",
      "first_name": "Janet",
      "last_name": "Weaver",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
    },
    {
      "id": 3,
      "email": "emma.wong@reqres.in",
      "first_name": "Emma",
      "last_name": "Wong",
      "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
    }
  ]
}
5. Make Employee Model

Create a model class named is Employee.java and paste the below code inside this file. This is a model class that contains a name, email, id, etc.

package com.example.databinding.model;

import android.widget.ImageView;
import androidx.databinding.BindingAdapter;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.example.databinding.R;
import com.google.gson.annotations.SerializedName;

public class Employee {

  @SerializedName("avatar")
  private String avatar;
  @SerializedName("email")
  private String email;
  @SerializedName("first_name")
  private String firstName;
  @SerializedName("id")
  private Long id;
  @SerializedName("last_name")
  private String lastName;

  public String getAvatar() {
    return avatar;
  }

  public void setAvatar(String avatar) {
    this.avatar = avatar;
  }

  public String getEmail() {
    return email;
  }

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

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  // important code for loading image here
  @BindingAdapter({ "avatar" })
  public static void loadImage(ImageView imageView, String imageURL) {
    Glide.with(imageView.getContext())
        .setDefaultRequestOptions(new RequestOptions()
            .circleCrop())
        .load(imageURL)
        .placeholder(R.drawable.loading)
        .into(imageView);
  }
}

Let’s see the above class. In this class, we are adding one method name loadImage() which annotated with @BindingAdapter({ “avatar” }) . Actually, here I’m binding avatar image with ImageView. It is using glide for loading images.

6. Make EmployeeDBResponse Model

Create an EmployeeDBResponse.java model class. I take name EmployeeDBResponse that mean this is server database response. This model class contains employee list and no of page etc.

package com.example.databinding.model;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.List;

public class EmployeeDBResponse {
  @SerializedName("page")
  @Expose
  private Integer page;
  @SerializedName("per_page")
  @Expose
  private Integer perPage;
  @SerializedName("total")
  @Expose
  private Integer total;
  @SerializedName("total_pages")
  @Expose
  private Integer totalPages;
  @SerializedName("data")
  @Expose
  private List<Employee> employee = null;

  public Integer getPage() {
    return page;
  }

  public void setPage(Integer page) {
    this.page = page;
  }

  public Integer getPerPage() {
    return perPage;
  }

  public void setPerPage(Integer perPage) {
    this.perPage = perPage;
  }

  public Integer getTotal() {
    return total;
  }

  public void setTotal(Integer total) {
    this.total = total;
  }

  public Integer getTotalPages() {
    return totalPages;
  }

  public void setTotalPages(Integer totalPages) {
    this.totalPages = totalPages;
  }

  public List<Employee> getEmployee() {
    return employee;
  }

  public void setEmployee(List<Employee> employee) {
    this.employee = employee;
  }
}

7. Designing Employee List Item

Let’s create a layout to display list item. This layout file contains TextViews for the display employee details. I have put all layout content inside the <layout/> tag. Basically, data binding layout we have to put inside <layout/> tag.

Inside the <layout/> tag, I have declared an Employee object inside <variable/> tag. The variable name is an employee. Using this variable I have set the value of each widget, Such as I have set user name like

android:text="@{employee.firstName.concat(@string/space).concat(employee.lastName)}".  

For profile picture

bind:avatar="@{employee.avatar}"
8. Full XML file looks like this
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
  <data>
    <variable
        name="employee"
        type="com.example.databinding.model.Employee"/>

  </data>
  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:adjustViewBounds="true">

    <androidx.cardview.widget.CardView
        android:id="@+id/cvEmployee"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:elevation="3dp"
        card_view:cardCornerRadius="1dp">

      <androidx.constraintlayout.widget.ConstraintLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/ivPic"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_marginBottom="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:scaleType="fitXY"
            bind:avatar="@{employee.avatar}"
            bind:layout_constraintBottom_toBottomOf="parent"
            bind:layout_constraintStart_toStartOf="parent"
            bind:layout_constraintTop_toTopOf="parent"
            android:layout_marginLeft="8dp"/>

        <TextView
            android:id="@+id/tvFullName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_below="@+id/ivPic"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:padding="4dp"
            android:text="@{employee.firstName.concat(@string/space).concat(employee.lastName)}"
            android:textColor="@color/colorPrimary"
            android:textSize="18sp"
            bind:layout_constraintEnd_toEndOf="parent"
            bind:layout_constraintStart_toEndOf="@+id/ivPic"
            bind:layout_constraintTop_toTopOf="parent"
            tools:text="Morris"/>

        <TextView
            android:id="@+id/tvEmail"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tvFullName"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="4dp"
            android:padding="4dp"
            android:text="@{`Email - ` + employee.email}"
            android:textColor="@color/colorAccent"
            android:textSize="16sp"
            bind:layout_constraintEnd_toEndOf="parent"
            bind:layout_constraintStart_toEndOf="@+id/ivPic"
            bind:layout_constraintTop_toBottomOf="@+id/tvFullName"
            tools:text="morris@gmail.com"/>

      </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
  </LinearLayout>
</layout>

9. Implementing Employee Data Adapter

Let’s create a RecyclerView Adapter class for holding employee list items. In a normal class, we need to bind the view with corresponding data object inside onBindViewHolder() methods. In this case, we already bind employee model data inside the layout file.

Now we have to attached item layout using data binding onCreateViewHolder() and set the item data over the employee object.

package com.example.databinding.adapter;

import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.example.databinding.R;
import com.example.databinding.databinding.EmployeeListItemBinding;
import com.example.databinding.model.Employee;
import java.util.ArrayList;

public class EmployeeDataAdapter
    extends RecyclerView.Adapter<EmployeeDataAdapter.EmployeeViewHolder> {

  private ArrayList<Employee> employees;

  @NonNull
  @Override
  public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    EmployeeListItemBinding employeeListItemBinding =
        DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()),
            R.layout.employee_list_item, viewGroup, false);
    return new EmployeeViewHolder(employeeListItemBinding);
  }

  @Override
  public void onBindViewHolder(@NonNull EmployeeViewHolder employeeViewHolder, int i) {
    Employee currentStudent = employees.get(i);
    employeeViewHolder.employeeListItemBinding.setEmployee(currentStudent);
  }

  @Override
  public int getItemCount() {
    if (employees != null) {
      return employees.size();
    } else {
      return 0;
    }
  }

  public void setEmployeeList(ArrayList<Employee> employees) {
    this.employees = employees;
    notifyDataSetChanged();
  }

  class EmployeeViewHolder extends RecyclerView.ViewHolder {

    private EmployeeListItemBinding employeeListItemBinding;

    public EmployeeViewHolder(@NonNull EmployeeListItemBinding employeetListItemBinding) {
      super(employeetListItemBinding.getRoot());

      this.employeeListItemBinding = employeetListItemBinding;
    }
  }
}

11. Setup Retrofit Interface