Keep Android Apps Lean: Optimally Utilize Data Binding and RecyclerView

Introduction to Android RecyclerView with Data Binding

In this article, we show how to integrate RecyclerView into an Android app with Data Binding. Data Binding significantly reduces boilerplate code, making development more efficient and readable. Here, we explain how to use Data Binding in combination with the ViewHolder pattern and how adapter classes can be easily generalized as a result. Finally, we demonstrate how to pass an adapter object directly in the XML code.

Let’s Get Started: First Steps

Before we begin, we need to ensure that Data Binding is enabled in our Android app. To do this, we add the following code to the build.gradle file:

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

Additionally, we add the following dependency:

implementation 'com.android.support:design:28.0.0'

Project Structure

In the following application, we will load the data directly from the XML into the adapter rows of the RecyclerView. We will also set the onClickListener methods directly in the layout rows.

Code for the Data Model

The code for the DataModel.java class is as follows:

package com.example.androidrecyclerviewdatabinding;

public class DataModel {

    public String androidVersion, androidName;

    public DataModel(String androidName, String androidVersion) {
        this.androidName = androidName;
        this.androidVersion = androidVersion;
    }
}

Layout for the Main Activity

The layout for the activity_main.xml file is defined as follows:

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

    <data>
        <!-- Data could be bound here -->
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>

Main Activity in Java

The code for the MainActivity.java file is as follows:

package com.example.androidrecyclerviewdatabinding;

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

import com.example.androidrecyclerviewdatabinding.databinding.ActivityMainBinding;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        populateData();
    }

    private void populateData() {
        List dataModelList = new ArrayList<>();

        dataModelList.add(new DataModel("Android Oreo", "8.1"));
        dataModelList.add(new DataModel("Android Pie", "9.0"));
        dataModelList.add(new DataModel("Android Nougat", "7.0"));
        dataModelList.add(new DataModel("Android Marshmallow", "6.0"));

        MyRecyclerViewAdapter myRecyclerViewAdapter = new MyRecyclerViewAdapter(dataModelList, this);
        binding.setMyAdapter(myRecyclerViewAdapter);
    }
}

Layout for Each Row in the RecyclerView

The layout for each row in the RecyclerView is defined in item_row.xml:

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

    <data>
        <variable
            name="model"
            type="com.example.androidrecyclerviewdatabinding.DataModel" />
        <variable
            name="itemClickListener"
            type="com.example.androidrecyclerviewdatabinding.CustomClickListener" />
    </data>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="@{() -> itemClickListener.cardClicked(model)}"
        app:cardUseCompatPadding="true">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_margin="8dp"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tvAndroidName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{model.androidName}"
                android:textAppearance="@style/TextAppearance.AppCompat.Headline" />

            <TextView
                android:id="@+id/tvAndroidVersion"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{model.androidVersion}"
                android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />

        </LinearLayout>

    </android.support.v7.widget.CardView>

</layout>

In the above layout, the DataModel reference and a reference to the CustomClickListener interface, whose method is called within the CardView, are passed.

CustomClickListener Interface

The CustomClickListener.java interface is as follows:

package com.example.androidrecyclerviewdatabinding;

public interface CustomClickListener {
    void cardClicked(DataModel f);
}

Adapter Class for RecyclerView

The adapter class MyRecyclerViewAdapter.java:

package com.example.androidrecyclerviewdatabinding;

import android.content.Context;
import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.Toast;

import java.util.List;

import com.example.androidrecyclerviewdatabinding.databinding.ItemRowBinding;

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> implements CustomClickListener {

    private List<DataModel> dataModelList;
    private Context context;

    public MyRecyclerViewAdapter(List<DataModel> dataModelList, Context ctx) {
        this.dataModelList = dataModelList;
        context = ctx;
    }

    @Override
    public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                               int viewType) {
        ItemRowBinding binding = DataBindingUtil.inflate(
                LayoutInflater.from(parent.getContext()),
                R.layout.item_row, parent, false);

        return new ViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        DataModel dataModel = dataModelList.get(position);
        holder.bind(dataModel);
        holder.itemRowBinding.setItemClickListener(this);
    }

    @Override
    public int getItemCount() {
        return dataModelList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ItemRowBinding itemRowBinding;

        public ViewHolder(ItemRowBinding itemRowBinding) {
            super(itemRowBinding.getRoot());
            this.itemRowBinding = itemRowBinding;
        }

        public void bind(Object obj) {
            itemRowBinding.setVariable(BR.model, obj);
            itemRowBinding.executePendingBindings();
        }
    }

    public void cardClicked(DataModel f) {
        Toast.makeText(context, "You clicked " + f.androidName,
                Toast.LENGTH_LONG).show();
    }
}

Bind Adapter Directly in XML

Thanks to Data Binding, the adapter can now be passed directly in the XML code, further simplifying the code in MainActivity.java. The adapter is set in activity_main.xml as follows:

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

    <data>
        <variable
            name="myAdapter"
            type="com.example.androidrecyclerviewdatabinding.MyRecyclerViewAdapter" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adapter="@{myAdapter}"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>

In MainActivity.java, the adapter is now set as follows:

MyRecyclerViewAdapter myRecyclerViewAdapter = new MyRecyclerViewAdapter(dataModelList, this);
binding.setMyAdapter(myRecyclerViewAdapter);

With this technique, we no longer need to manually initialize the RecyclerView in the activity. This makes the code cleaner and the application more efficient.

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: