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 Name | RecyclerViewDemo |
---|---|
Package Name | com.pcsalt.recyclerviewdemo |
Minimum SDK Version | 21 |
Activity Type | Empty Activity |
Activity Name | MainActivity |
Layout Name | activity_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(); } } }