Android RecyclerView Example
1. What is RecyclerView?
Google’s upcoming operating system named Android L looks very promising. It is highly focused on rich user experience and what they called it as material design. In this example we will take a look at the new UI widget called RecyclerView
.
RecyclerView
is more advanced and flexible and efficient version of ListView. RecyclerView ViewGroup is an container for larger data set of views that can be recycled and scrolled very efficiently. RecyclerView can be used for larger datasets to be rendered on the UI like a list. RecyclerView provides maximum flexibility to design different kind of views
2. RecyclerView Example
The tutorial, download the feed data from server, parse the JSON feed response and display the items on list. This example using both RecyclerView and CardView for creating the UI as shown in the following video.
To accomplish this result as shown in the above video, we need to follow the following steps:
- Create a new Android application in Android Studio IDE and add the support library dependency.
- Declare an layout for your activity and add a RecyclerView and ProgressBar widget
- Create an activity class to initiate data download, initialize the adapter and display data on RecyclerView
- Create RecyclerView row layout. Here we will use the CardView widget.
- Create an custom adapter that will be used for RecyclerView
- Implementing RecyclerView click event handling
2.1. Adding Support Library
Android SDK doesn’t includes the RecyclerView
class, and hence for using RecyclerView in your project you first need to add the recycler view support library to your project. Android Studio users can add the following graddle dependency to project build.graddle
file.
dependencies { compile 'com.android.support:recyclerview-v7:+' compile 'com.android.support:cardview-v7:21.0.+' compile project(':picasso-2.3.4') }
Notice that in the above dependency declaration, I have added the RecyclerView and CardView support library, and the Picasso.jar
. Before this, you need to add Picasso jar module by going to your Android Studio Project properties.
2.2. Adding Internet Permission
You might be aware that, Android application must declare all the permissions that are required for application. As we need to download the data form server, we need to add the INTERNET
permission. Add the following line toAndroidManifest.xml
file.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.javatechig.app"> <uses-permission android:name="android.permission.INTERNET" /> <application ..... </application> </manifest>
2.3. Declaring Activity Layout
Our first step towards this example is to declare the activity layout. Here in this example, we will add a RecyclerView and ProgressBar inside aRelativeLayout
. The progress bar will be shown to user while the data is being downloaded. Add the following code snippets tolayout/activity_feed_list.xml
file.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/activity_vertical_margin"> <view android:id="@+id/recycler_view" class="android.support.v7.widget.RecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" /> <ProgressBar android:id="@+id/progress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout>
2.4. Activity Using RecyclerView
RecyclerView
is a ViewGroup similar to ListView
or GridView
. A ViewGroup is an UI widget that is used to render collection of data. In this example, we are trying to download the latest feeds from this site and display it on RecyclerView. The focus of this tutorial is narrow down to RecyclerView, hence it doesn’t include any explanation for download and parse data from server. For learning how to download data from server, you may read Android Networking Tutorial.
As the data is downloaded, inside onPostExecute()
we are initializing the adapter and setting adapter to RecyclerView instance by just calling setAdapter()
method.
package com.javatechig.app; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.widget.ProgressBar; import android.widget.Toast; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; public class FeedListActivity extends AppCompatActivity { private static final String TAG = "RecyclerViewExample"; private List<FeedItem> feedsList; private RecyclerView mRecyclerView; private MyRecyclerAdapter adapter; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_feeds_list); // Initialize recycler view mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); progressBar = (ProgressBar) findViewById(R.id.progress_bar); progressBar.setVisibility(View.VISIBLE); // Downloading data from below url final String url = "http://javatechig.com/?json=get_recent_posts&count=45"; new AsyncHttpTask().execute(url); } public class AsyncHttpTask extends AsyncTask<String, Void, Integer> { @Override protected void onPreExecute() { setProgressBarIndeterminateVisibility(true); } @Override protected Integer doInBackground(String... params) { Integer result = 0; HttpURLConnection urlConnection; try { URL url = new URL(params[0]); urlConnection = (HttpURLConnection) url.openConnection(); int statusCode = urlConnection.getResponseCode(); // 200 represents HTTP OK if (statusCode == 200) { BufferedReader r = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = r.readLine()) != null) { response.append(line); } parseResult(response.toString()); result = 1; // Successful } else { result = 0; //"Failed to fetch data!"; } } catch (Exception e) { Log.d(TAG, e.getLocalizedMessage()); } return result; //"Failed to fetch data!"; } @Override protected void onPostExecute(Integer result) { // Download complete. Let us update UI progressBar.setVisibility(View.GONE); if (result == 1) { adapter = new MyRecyclerAdapter(FeedListActivity.this, feedsList); mRecyclerView.setAdapter(adapter); } else { Toast.makeText(FeedListActivity.this, "Failed to fetch data!", Toast.LENGTH_SHORT).show(); } } } private void parseResult(String result) { try { JSONObject response = new JSONObject(result); JSONArray posts = response.optJSONArray("posts"); feedsList = new ArrayList<>(); for (int i = 0; i < posts.length(); i++) { JSONObject post = posts.optJSONObject(i); FeedItem item = new FeedItem(); item.setTitle(post.optString("title")); item.setThumbnail(post.optString("thumbnail")); feedsList.add(item); } } catch (JSONException e) { e.printStackTrace(); } } }
Notice that in the above code snippet, we are using the FeedItem
POJO class to parse the data from server. Create a new file named FeedItem.java
class in your project source folder and add the following snippets.
package com.javatechig.recyclerview; public class FeedItem { private String title; private String thumbnail; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getThumbnail() { return thumbnail; } public void setThumbnail(String thumbnail) { this.thumbnail = thumbnail; } }
2.5. Creating RecyclerView Adapter
Android RecyclerView includes special kind of adapter which works pretty much same as traditional Android adapters but with additional functionalities. The additional functionalities includes;
- It adds two new methods like
onCreateViewHolder()
andonBindViewHolder()
to organize the code. You must override these two methods for inflate the view and to bind data to the view - Implements a ViewHolder by default. Conceptually
RecyclerView.ViewHolder
works same as the ViewHolder design pattern which we have been using with other Adapters - Takes care of the overhead of recycling and gives better performance and scrolling
package com.javatechig.app; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.squareup.picasso.Picasso; import java.util.List; public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.CustomViewHolder> { private List<FeedItem> feedItemList; private Context mContext; public MyRecyclerAdapter(Context context, List<FeedItem> feedItemList) { this.feedItemList = feedItemList; this.mContext = context; } @Override public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_row, null); CustomViewHolder viewHolder = new CustomViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(CustomViewHolder customViewHolder, int i) { FeedItem feedItem = feedItemList.get(i); //Download image using picasso library Picasso.with(mContext).load(feedItem.getThumbnail()) .error(R.drawable.placeholder) .placeholder(R.drawable.placeholder) .into(customViewHolder.imageView); //Setting text view title customViewHolder.textView.setText(Html.fromHtml(feedItem.getTitle())); } @Override public int getItemCount() { return (null != feedItemList ? feedItemList.size() : 0); } }
2.6. RecyclerView Row Layout
The above code mentions about another new layout list_row.xml
. This is defies the layout for RecyclerView item. Here in this example, we are using the CardView as parent layout and CardView hosts a RelativeLayout with an ImageView and TextView.
Click here to learn more about Android CardView.
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cardview="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="80dp" android:layout_margin="8dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="80dp"> <ImageView android:id="@+id/thumbnail" android:layout_width="100dp" android:layout_height="80dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:scaleType="centerCrop" android:src="@drawable/placeholder" /> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@+id/thumbnail" android:maxLines="3" android:padding="8dp" android:text="dafdafda" android:textColor="#222" android:textSize="15dp" /> </RelativeLayout> </android.support.v7.widget.CardView>
2.6. Implementing ViewHolder Pattern
Now let us create our ViewHolder class. The ViewHolder contains the reference to the each of the ui widget on the row. We have defined this class as a private class inside MyRecyclerAdapter
.
public class CustomViewHolder extends RecyclerView.ViewHolder { protected ImageView imageView; protected TextView textView; public CustomViewHolder(View view) { super(view); this.imageView = (ImageView) view.findViewById(R.id.thumbnail); this.textView = (TextView) view.findViewById(R.id.title); } }
3. Handle RecyclerView Click Event
Handling click event on RecyclerView is not as sweet as handling click listener in ListView or GridView. Android RecyclerView doesn’t provide any built in listeners or handy way of handling click events.
However we can use workaround to handle click event explicitly by adding the following code in onBindViewHolder()
method.
//Handle click event on both title and image click customViewHolder.textView.setOnClickListener(clickListener); customViewHolder.imageView.setOnClickListener(clickListener); customViewHolder.textView.setTag(customViewHolder); customViewHolder.imageView.setTag(customViewHolder);
Add declare the clickListener variable as follows
View.OnClickListener clickListener = new View.OnClickListener() { @Override public void onClick(View view) { CustomViewHolder holder = (CustomViewHolder) view.getTag(); int position = holder.getPosition(); FeedItem feedItem = feedItemList.get(position); Toast.makeText(mContext, feedItem.getTitle(), Toast.LENGTH_SHORT).show(); } };