Quantcast
Channel: 懒得折腾
Viewing all articles
Browse latest Browse all 764

Android RecyclerView Example

$
0
0

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:

  1. Create a new Android application in Android Studio IDE and add the support library dependency.
  2. Declare an layout for your activity and add a RecyclerView and ProgressBar widget
  3. Create an activity class to initiate data download, initialize the adapter and display data on RecyclerView
  4. Create RecyclerView row layout. Here we will use the CardView widget.
  5. Create an custom adapter that will be used for RecyclerView
  6. 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. ConceptuallyRecyclerView.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();
    }
};

4. Download Source Code

Download from GitHub.



Viewing all articles
Browse latest Browse all 764

Trending Articles