RecyclerView – Android

In this post, we are going to load the list of posts from PCSalt.com. In our previous post, JSON Parsing – Android, we used ListView to display a list of posts. But, in this post, we are going to use RecyclerView to display it.


Almost all parsing and model classes are used from that post. If you haven’t followed that post, don’t worry. We are going to write all code here. This project has been uploaded to GitHub.
Create a new Android project using following information.

Project NameRecyclerViewDemo
Package Namecom.pcsalt.recyclerviewdemo
Minimum SDK Version21
Activity TypeEmpty Activity
Activity NameMainActivity
Layout Nameactivity_name

Adding required permissions in AndroidManifest.xml

This app will fetch the list of the post using Internet. So, provide the following permissions in AndroidManifest.xml. Otherwise, the app will not fetch the list.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

Adding dependencies in build.gradle

We will be using components like RecyclerView, CardView and resources from appcompat. So, add following dependencies in build.gradle located in RecyclerViewDemo/app/build.gradle.

dependencies {
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:recyclerview-v7:23.0.1'
    compile 'com.android.support:cardview-v7:23.0.1'
}

Create Model for Parsed data

The JSON we are going to parse contains a title, guid and date in a JSONObject.

package com.pcsalt.recyclerviewdemo;
public class PostValue {
    private String title, guid, date;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getGuid() {
        return guid;
    }
    public void setGuid(String guid) {
        this.guid = guid;
    }
    public String getDate() {
        return date;
    }
    public void setDate(String date) {
        this.date = date;
    }
}

Create JSON Parser

The following parser will loop through JSONObject and JSONArray and save values in List<PostValue>. This list could be used later to display it in RecyclerView.

package com.pcsalt.recyclerviewdemo;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class JSONParser {
    public List parse(JSONObject jsonObject) {
        List postList = new ArrayList<>();
        PostValue postValue;
        try {
            JSONArray postsArray = jsonObject.getJSONArray("posts");
            for (int i = 0; i < postsArray.length(); i++) {
                JSONObject posts = postsArray.getJSONObject(i);
                JSONObject post = posts.getJSONObject("post");
                postValue = new PostValue();
                String title = post.getString("post_title");
                String guid = post.getString("guid");
                String date = post.getString("post_date");
                postValue.setTitle(title);
                postValue.setGuid(guid);
                postValue.setDate(date);
                postList.add(postValue);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return postList;
    }
}

Create connection with post URL

Create URLConnection to get JSON response.

package com.pcsalt.recyclerviewdemo;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class JSONHelper {
    private static final String URL_MAIN = "http://pcsalt.com/postservice/?format=json";
    private static final String TAG = JSONHelper.class.getSimpleName();
    private JSONObject mJsonObject = null;
    private String json = "";
    public JSONObject getJSONFromUrl() {
        try {
            URL url = new URL(URL_MAIN);
            URLConnection urlConnection = url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    urlConnection.getInputStream()));
            StringBuilder builder = new StringBuilder();
            String line;
            while ((line = in.readLine()) != null) {
                builder.append(line).append("\n");
            }
            json = builder.toString();
            in.close();
        } catch (Exception e) {
            Log.e(TAG, "Exception: " + e.getMessage());
            e.printStackTrace();
        }
        try {
            // Convert the JSON String from InputStream to a JSONObject
            mJsonObject = new JSONObject(json);
        } catch (JSONException e) {
            Log.e(TAG, "Exception: " + e.getMessage());
        }
        return mJsonObject;
    }
}

Now we have everything to parse and data is ready to be displayed in RecyclerView. Before moving forward towards writing Adapter, RecyclerView click listener and other required classes, we are going to create XML for list item and RecyclerView.

Create list item layout

In this list item, all elements are wrapped inside CardView to provide CardLayout view. List item contains two items:
– Post title
– Publish date
File name: list_item_post.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="2dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="2dp"
    android:clickable="true"
    android:foreground="?android:attr/selectableItemBackground">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="5dp">
        <TextView
            android:id="@+id/tvTitle"
            style="?android:textAppearanceMedium"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:maxLines="2"
            android:textColor="#ff5a595b" />
        <TextView
            android:id="@+id/tvPublishDate"
            style="?android:textAppearanceSmall"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textColor="#ff80cbc4" />
    </LinearLayout>
</android.support.v7.widget.CardView>

Adding RecyclerView in activity_main.xml

Add RecyclerView in activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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" />
</RelativeLayout>

Create PostAdapter to display list of posts

While creating adapter for RecyclerView, keep following things in mind:
– The adapter must extend RecyclerView.Adapter<RecyclerView.ViewHolder> or its child,
– The adapter must have a ViewHolder which extends RecyclerView.ViewHolder

package com.pcsalt.recyclerviewdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class PostAdapter extends RecyclerView.Adapter {
    private List postList;
    private LayoutInflater inflater;
    public PostAdapter(Context context, List postList) {
        this.postList = postList;
        this.inflater = LayoutInflater.from(context);
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.list_item_post, parent, false);
        return new ViewHolder(view);
    }
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        PostValue currentPost = postList.get(position);
        holder.tvTitle.setText(currentPost.getTitle());
        holder.tvPublishDate.setText(currentPost.getDate());
    }
    @Override
    public int getItemCount() {
        return postList == null ? 0 : postList.size();
    }
    class ViewHolder extends RecyclerView.ViewHolder {
        TextView tvTitle, tvPublishDate;
        public ViewHolder(View itemView) {
            super(itemView);
            tvTitle = (TextView) itemView.findViewById(R.id.tvTitle);
            tvPublishDate = (TextView) itemView.findViewById(R.id.tvPublishDate);
        }
    }
}

Adding click listener for RecyclerView

RecyclerView does not have Click Listener. So, we have to implement our own list item click listener. This could be done by modifying provided RecyclerView.OnItemTouchListener.

Create abstract class RecyclerClickListener

We are creating abstract class because of the obvious reason. We don’t have to override both of the methods to implement Click event only.

package com.pcsalt.recyclerviewdemo.listener;
import android.view.View;
public abstract class RecyclerClickListener {
    public void onClick(View view, int position) {
    }
    public void onLongClick(View view, int position) {
    }
}

Create custom RecyclerTouchListener

This class will intercept touch event on RecyclerView item. And, based on Gesture it would decide whether it is a single click or long click.

package com.pcsalt.recyclerviewdemo.listener;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
    private GestureDetector gestureDetector;
    private RecyclerClickListener clickListener;
    private boolean disallowIntercept = false;
    public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final RecyclerClickListener clickListener) {
        this.clickListener = clickListener;
        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
            @Override
            public void onLongPress(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null && clickListener != null) {
                    clickListener.onLongClick(child, recyclerView.getChildLayoutPosition(child));
                }
            }
        });
    }
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        View child = rv.findChildViewUnder(e.getX(), e.getY());
        if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
            clickListener.onClick(child, rv.getChildLayoutPosition(child));
        }
        return false;
    }
    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }
    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        this.disallowIntercept = disallowIntercept;
    }
}

Add code in MainActivity to display list of post

MainActivity.java will be performing following tasks:
– Link all views mentioned in its layout XML (in this case RecyclerView),
– Start a background task (AsyncTask) to start loading the list of posts
– After loading is finished, it would inflate data in RecyclerView and display list
– Attach a click event for all list items, which would open the relevant web page in a browser.

package com.pcsalt.recyclerviewdemo;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.pcsalt.recyclerviewdemo.listener.RecyclerClickListener;
import com.pcsalt.recyclerviewdemo.listener.RecyclerTouchListener;
import org.json.JSONObject;
import java.util.List;
public class MainActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    AppCompatActivity activity = MainActivity.this;
    List postList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        new JSONAsync().execute();
    }
    private void initViews() {
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(activity);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addOnItemTouchListener(new RecyclerTouchListener(activity, recyclerView, new RecyclerClickListener() {
            @Override
            public void onClick(View view, int position) {
                if (postList != null) {
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse(postList.get(position).getGuid()));
                    startActivity(intent);
                }
            }
        }));
    }
    class JSONAsync extends AsyncTask<Void, Void, Void> {
        ProgressDialog pd;
        @Override
        protected void onPreExecute() {
            pd = ProgressDialog.show(MainActivity.this, null, "Loading posts for PCSalt.com ...", true, false);
        }
        @Override
        protected Void doInBackground(Void... params) {
            JSONObject jsonObject = new JSONHelper().getJSONFromUrl();
            postList = new JSONParser().parse(jsonObject);
            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            PostAdapter postAdapter = new PostAdapter(activity, postList);
            recyclerView.setAdapter(postAdapter);
            pd.dismiss();
        }
    }
}

Download source code

Download source code from GitHub