Android ViewHolder Pattern Example

Now we are going to code the smooth scrolling of our Android ListView. In the previous post, we tried to understand how the ListView with adapter works. This time, it will be all about performance.

I did this a separate post because Android ListView is difficult to understand at times. What I have in mind is, “we have to do the basics first, and then apply the optimization.”

What’s with the ViewHolder pattern?

The ViewHolder design pattern enables you to access each list item view without the need for the look up, saving valuable processor cycles. Specifically, it avoids frequent call of findViewById() during ListView scrolling, and that will make it smooth.

Without the ViewHolder Design Pattern

Okay, let’s dig it out and see how it works without the ViewHolder pattern.

Let’s take a look at our previous getView() method in ArrayAdapterItem.java

  1. The first time it was loaded, convertView is null. We’ll have to inflate our list item layout and find the TextView via findViewById().
  2. The second time it was loaded, convertView is not null, good! We don’t have to inflate it again. But we’ll use findViewById() again.
  3. The following times it was loaded, convertView is definitely not null. But findViewById() is constantly called, it will work but, it slows down the performance especially if you have lots of items and Views in your ListView.

With the ViewHolder Design Pattern

Now let’s see how it works with the ViewHolder pattern.

  1. The first time it was loaded, convertView is null. We’ll have to inflate our list item layout, instantiate the ViewHolder, find the TextView via findViewById() and assign it to the ViewHolder, and set the ViewHolder as tag of convertView.
  2. The second time it was loaded, convertView is not null, good! We don’t have to inflate it again. And here’s the sweet thing, we won’t have to call findViewById() since we can now access the TextView via its ViewHolder.
  3. The following time it was loaded, convertView is definitely not null. The findViewById() is never called again, and that makes our smooth ListView scrolling.

Let’s Code!

So here it is, we’ll make use of the Android ViewHolder pattern in our ListView (in just 3 steps!).

Step 1: Add the following static class on our ArrayAdapterItem.java file

// our ViewHolder.
// caches our TextView
static class ViewHolderItem {
    TextView textViewItem;
}

Step 2: Our getView() will now look like this:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolderItem viewHolder;

    /*
     * The convertView argument is essentially a "ScrapView" as described is Lucas post 
     * http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
     * It will have a non-null value when ListView is asking you recycle the row layout. 
     * So, when convertView is not null, you should simply update its contents instead of inflating a new row layout.
     */
    if(convertView==null){

        // inflate the layout
        LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
        convertView = inflater.inflate(layoutResourceId, parent, false);

        // well set up the ViewHolder
        viewHolder = new ViewHolderItem();
        viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.textViewItem);

        // store the holder with the view.
        convertView.setTag(viewHolder);

    }else{
        // we've just avoided calling findViewById() on resource everytime
        // just use the viewHolder
        viewHolder = (ViewHolderItem) convertView.getTag();
    }

    // object item based on the position
    ObjectItem objectItem = data[position];

    // assign values if the object is not null
    if(objectItem != null) {
        // get the TextView from the ViewHolder and then set the text (item name) and tag (item ID) values
        viewHolder.textViewItem.setText(objectItem.itemName);
        viewHolder.textViewItem.setTag(objectItem.itemId);
    }

    return convertView;

}

Step 3: For the sake of testing, we’re going to put thousands of items in our ListView. On our MainActivity.java, our showPopUp() will now look like this:

public void showPopUp(){

    // we'll specify the number of items we want our ListView to have.
    int numberOfItems = 1000;

    // add your items, this can be done programatically
    // your items can be from a database
    ObjectItem[] ObjectItemData = new ObjectItem[numberOfItems];

    // we'll use a for loop 
    // created objects = number of items specified above
    for(int x=0; x<numberOfItems; x++){

        int sampleId = 90 + x;
        ObjectItemData[x] = new ObjectItem(sampleId, "Store # " + (x+1));

    }

    // our adapter instance
    ArrayAdapterItem adapter = new ArrayAdapterItem(this, R.layout.list_view_row_item, ObjectItemData);

    // create a new ListView, set the adapter and item click listener
    ListView listViewItems = new ListView(this);
    listViewItems.setAdapter(adapter);
    listViewItems.setOnItemClickListener(new OnItemClickListenerListViewItem());

    // put the ListView in the pop up
    alertDialogStores = new AlertDialog.Builder(MainActivity.this)
        .setView(listViewItems)
        .setTitle("Stores")
        .show();

}

I tested this code with as much as 2,000 items, and the performance is still smooth and great.

What’s Next?

If you have other ideas regarding this topic, please drop it in the comment section below. I’m more than willing to update this post and improve the life of mankind.

In the next post, we’ll try to use the AsyncTask to load image into the ListView. Something like how the Google Play Store app does it.
 

Reference: Android ViewHolder Pattern Example from our JCG partner Mike Dalisay at the The Code of a Ninja blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

8 Responses to "Android ViewHolder Pattern Example"

  1. Bill Mote says:

    Interested in why you named your class ViewHolderItem? Every implementation I’ve ever seen (and that’s a lot) uses ViewHolder. Why deviate from the norm in this case?

  2. J & J says:

    What will be a solution when row views are no equal? Where they can be cached instead of inflating each time?

  3. rahul bawa says:

    import java.util.ArrayList;

    import android.content.Context;
    import android.content.res.Resources;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;

    public class TypeOfConcurAdapter extends BaseAdapter {
    private Context context;
    private ArrayList typeOfConcur;
    private int size;
    private LayoutInflater inflater = null;
    private Holder holder;
    private ConcurType concurType;

    public TypeOfConcurAdapter(Context context,
    ArrayList typeOfConcur) {
    // TODO Auto-generated constructor stub
    this.context = context;
    this.typeOfConcur = typeOfConcur;
    this.size = this.typeOfConcur.size();
    this.inflater = (LayoutInflater) this.context
    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
    // TODO Auto-generated method stub
    return this.size;
    }

    @Override
    public Object getItem(int pos) {
    // TODO Auto-generated method stub
    return this.typeOfConcur.get(pos);
    }

    @Override
    public long getItemId(int id) {
    // TODO Auto-generated method stub
    return id;
    }

    @Override
    public View getView(final int pos, final View recycle, ViewGroup parent) {
    // TODO Auto-generated method stub
    View convertView = recycle;
    concurType = typeOfConcur.get(pos);
    if (convertView == null) {

    convertView = inflater.inflate(R.layout.type_of_concur_item, null);
    holder = new Holder();

    holder.concurLabel = (TextView) convertView
    .findViewById(R.id.type_of_concur_item);
    holder.tickMark = (ImageView) convertView
    .findViewById(R.id.tick_mark);
    holder.tickMark.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View view) {
    // TODO Auto-generated method stub
    if (concurType.isTicked()) {
    concurType.setTicked(false);
    ((ImageView)view).setImageResource(R.drawable.ok_outline_xxhdpi);
    showLog(“onclick”,”true”);
    } else {
    concurType.setTicked(true);
    ((ImageView)view).setImageResource(R.drawable.ok_xxhdpi);
    showLog(“onclick”,”false”);
    }
    }
    });
    if (concurType.isTicked()) {
    holder.tickMark.setImageResource(R.drawable.ok_xxhdpi);
    showLog(“up”,”up-true”);
    } else {
    holder.tickMark.setImageResource(R.drawable.ok_outline_xxhdpi);
    showLog(“up”,”up-false”);
    }
    convertView.setTag(holder);
    }

    holder = (Holder) convertView.getTag();
    if (concurType.isTicked()) {
    showLog(“below”,”below-true”);
    holder.tickMark.setImageResource(R.drawable.ok_xxhdpi);
    } else {
    showLog(“below”, “below-false”);
    holder.tickMark.setImageResource(R.drawable.ok_outline_xxhdpi);

    }

    // update the view
    return convertView;
    }

    private void showLog(String string, String string2) {
    // TODO Auto-generated method stub
    Log.v(string,string2);
    }

    static class Holder {
    public TextView concurLabel;
    public ImageView tickMark;
    }

    }

    Hi can you help me out in this,its a gridView with custom objects(POJO) with properties like text and isTicked.

    heres what i am trying to achieve

    a row contains a textView and a checkbox-

    if a checkbox is already clicked and is clicked again it gets unchecked.

    but whats happening is that when i click a checkbox at position 1, then a random position checkbox gets checked/unchecked.its so crazy and is my code fault as i am not getting this gridView.Can you offer any help.

  4. Abdul says:

    hi ur tutorial is superb . I got more idea on this. but i have doubt how can you add image with like buttons inside listview i am working on this not able to get resolution on that.

    • Muhammad Ali Rafique says:

      viewHolder.ImageViewImagepath = (ImageView) view.findViewById(R.id.imageView1);
      viewHolder.ImageViewImagepath.setImageBitmap(myBitmap);

  5. rahul says:

    The problem with image buttons or normal buttons is that they consume the click instead of throwing click down to list item.So to avoid it use image view instead or aetfocussable on your image button to false.

  6. Why are you using

    ObjectItem objectItem = data[position];

    and not the supplied getItem method ie.

    ObjectItem objectItem = getItem(position);

    ??

  7. Muhammad Ali Rafique says:

    Its just for getting data to populate Text View its not related to View holder

Leave a Reply


2 + nine =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close