Using Data Binding with RecyclerView in Android

    On Google I/O 2015 announced new library Data Binding Library. Its main objective - removal of interaction of model and View in XML files. It considerably simplifies writing of code and relieves of need of use of the findViewById() methods, adding of links to view-elements in Activity/Fragment.

     As I have had some post about this library, through them, you have learned about binding local or online data to your views. In this article, we will look into how to use Data Binding with RecyclerView. So lets go ahead and see how it all works.
    Before start, adding RecyclerView and CardView dependencies and data binding library to your app-level build.gradle:
build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        applicationId "info.devexchanges.databindingrecyclerview"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    dataBinding {
        enabled = true
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.0'
    compile 'com.android.support:recyclerview-v7:24.2.0'
    compile 'com.android.support:cardview-v7:24.2.0'
}

Create your POJO class

    Define a POJO class named Country with 2 variables are id and name for your project:
Country.java
package info.devexchanges.databindingrecyclerview;

public class Country {

    private int id;
    private String name;

    public Country() {
    }

    public Country(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

Defining in XML file

    Each POJO features is displayed to the RecyclerView row, so open your it's layout file and add layout tag as root of it. It is necessary to have layout tag and data tag. It holds information about which POJO is using in layout:
item_recycler_view.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="country"
            type="info.devexchanges.databindingrecyclerview.Country" />
    </data>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        card_view:cardCornerRadius="2dp"
        card_view:contentPadding="10dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ImageView
                android:id="@+id/image"
                android:src="@drawable/world"
                android:layout_width="80dp"
                android:layout_height="80dp" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="10dp"
                android:layout_toRightOf="@id/image"
                android:orientation="vertical">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <TextView
                        android:layout_width="100dp"
                        android:layout_height="wrap_content"
                        android:text="ID"
                        android:textSize="22sp" />

                    <TextView
                        android:id="@+id/country_id"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@{String.valueOf(country.id)}"
                        android:textSize="22sp" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <TextView
                        android:layout_width="100dp"
                        android:layout_height="wrap_content"
                        android:text="Name"
                        android:textSize="22sp" />

                    <TextView
                        android:id="@+id/country_name"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@{country.name}"
                        android:textSize="22sp" />
                </LinearLayout>

            </LinearLayout>
        </RelativeLayout>
    </android.support.v7.widget.CardView>
</layout>
    As you can see, we'll bind id and name of Country to 2 TextViews later!

Configuring ViewHolder and RecyclerView adapter

    Now, we'll deal with the important works. To use item in RecyclerView, we require to create a custom ViewHolder. Create a new class and name it CountryViewHolder like this:
CountryViewHolder.java
package info.devexchanges.databindingrecyclerview;

import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import info.devexchanges.databindingrecyclerview.databinding.ItemRecyclerViewBinding;

public class CountryViewHolder extends RecyclerView.ViewHolder {
    private ItemRecyclerViewBinding binding;

    public CountryViewHolder(View view) {
        super(view);
        binding = DataBindingUtil.bind(view);
    }

    public void bind(Country country) {
        binding.setCountry(country);
    }
}
    Here, ItemRecyclerViewBiding class is auto-generated by project. When you use data tag in your layout, Android will auto generate a class with layout filename and a suffix “Binding”. In out case, layout file name is item_recycler_view so auto-generated class is ItemRecyclerViewBinding.
Create an adapter class for your RecyclerView:
RecyclerViewAdapter.java
package info.devexchanges.databindingrecyclerview;

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

import java.util.List;

import info.devexchanges.databindingrecyclerview.databinding.ItemRecyclerViewBinding;

public class RecyclerViewAdapter extends RecyclerView.Adapter<CountryViewHolder> {

    private List<Country> users;

    public RecyclerViewAdapter(List<Country> users) {
        this.users = users;
    }

    @Override
    public CountryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View statusContainer = inflater.inflate(R.layout.item_recycler_view, parent, false);

        return new CountryViewHolder(statusContainer);
    }

    @Override
    public void onBindViewHolder(CountryViewHolder holder, int position) {
        Country status = users.get(position);
        holder.bind(status);
    }

    @Override
    public int getItemCount() {
        return users.size();
    }
}
    Here, you just use holder.bind(status); code, this will actually bind your country object to your layout. There is nothing complex in this adapter class!

Activity programmatically code

    To make more this project more complexly, I will loading JSON data from an URL and set it to an ArrayList of Country. The main work of our activity is starting an AsyncTask and download/parsing data:
MainActivity.java
package info.devexchanges.databindingrecyclerview;

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

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private ArrayList<Country> countries;
    private RecyclerViewAdapter adapter;

    private final static String JSON_URL =
            "https://gist.githubusercontent.com/DevExchanges/29e8bb477bf520b3c076b1073a9fcd0f/raw/ad8fb0759cebde0e8ed492794ea8cc0bee184bb9/country.json";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

        countries = new ArrayList<>();
        adapter = new RecyclerViewAdapter(countries);
        recyclerView.setAdapter(adapter);
    }

    @Override
    protected void onStart() {
        super.onStart();

        //Load JSON data from an URL
        new GetCountriesFromURLTask(this, JSON_URL).execute();
    }

    public void parseJsonResponse(String result) {
        try {
            JSONObject json = new JSONObject(result);
            JSONArray jArray = new JSONArray(json.getString("message"));
            countries.clear();
            for (int i = 0; i < jArray.length(); i++) {

                JSONObject jObject = jArray.getJSONObject(i);
                Country country = new Country();
                country.setId(jObject.getInt("id"));
                country.setName(jObject.getString("name"));
                countries.add(country);
            }

            adapter.notifyDataSetChanged();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}
    And this is our custom AsyncTask:
GetCountriesFromURLTask.java
package info.devexchanges.databindingrecyclerview;

import android.app.ProgressDialog;
import android.os.AsyncTask;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GetCountriesFromURLTask extends AsyncTask<Void, Void, String> {

    private MainActivity activity;
    private String url;
    private ProgressDialog dialog;

    public GetCountriesFromURLTask(MainActivity activity, String url) {
        super();
        this.activity = activity;
        this.url = url;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // Create a progress dialog

        dialog = new ProgressDialog(activity);
        dialog.setTitle("Loading");
        dialog.setMessage("Loading data from URL...");
        dialog.setIndeterminate(false);
        dialog.show();
    }

    @Override
    protected String doInBackground(Void... params) {
        // call load JSON from url method
        return loadJSON(this.url);
    }

    @Override
    protected void onPostExecute(String result) {
        activity.parseJsonResponse(result);
        dialog.dismiss();
    }

    public String loadJSON(String url) {
        // Creating JSON Parser instance
        JSONParser jParser = new JSONParser();

        // getting JSON string from URL
        return jParser.getJSON(url, 20000);
    }

    private class JSONParser {
        public String getJSON(String url, int timeout) {
            HttpURLConnection urlConnection = null;
            try {
                URL u = new URL(url);
                urlConnection = (HttpURLConnection) u.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.setRequestProperty("Content-length", "0");
                urlConnection.setUseCaches(false);
                urlConnection.setAllowUserInteraction(false);
                urlConnection.setConnectTimeout(timeout);
                urlConnection.setReadTimeout(timeout);
                urlConnection.connect();
                int status = urlConnection.getResponseCode();

                switch (status) {
                    case 200:
                    case 201:
                        BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                        StringBuilder sb = new StringBuilder();
                        String line;
                        while ((line = br.readLine()) != null) {
                            sb.append(line + "\n");
                        }
                        br.close();
                        return sb.toString();
                }

            } catch (IOException ex) {
                Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            } finally {
                if (urlConnection != null) {
                    try {
                        urlConnection.disconnect();
                    } catch (Exception ex) {
                        Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
            return null;
        }
    }
}

Running application

    Before launching app, make sure that you provide Internet permission in your AndroidManifest.xml because it get online data:
<uses-permission android:name="android.permission.INTERNET" />
    We'll have this output:

Conclusions

    Through this post, you've learned about use Data Binding Library with RecyclerView. This is is a powerful tool, I suspect it will become more useful and more widely adopted. For now though, there are a few bugs and some kinks that need to be ironed out. After experimenting with it for a while, I am excited for what the library could potentially do in the future.
    Read more:

Share


Previous post
« Prev Post
Next post
Next Post »