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.”

android-ViewHolder-pattern-example

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.

Thanks for reading this Android ViewHolder Pattern Example!

Fixed: Android Dependencies – Missing facebooksdk.jar

Recently I was working with the Facebook SDK for Android and found the error:     Android     Dependencies     –     Missing     facebooksdk.jar. My project was unable to run and the eclipse console or logcat does not give a clear description of the error.

Home>Android Fixed: Android Dependencies – Missing facebooksdk.jar

But it can be seen when you try to view the properties > java build path > libraries tab > android dependencies of your project.
It looks like the project is looking for the facebooksdk.jar in the Facebook SDK bin folder. But it was missing. I wasn’t able to found a fix after around 30 minutes of googling the error. I don’t know, maybe my google skills are just failing me.
But the good thing is I was able to fixed this error myself. And here is the solution:

Right click on your project > properties > java compiler > compiler compliance level > select 1.7 on the dropdown.

Wait for a while while eclipse re-builds the workspace. Suddenly it was fixed!
Sorry I was too lazy to put up some screenshots, this is just a quick post that I thought can be useful to some devs. As always, thanks for reading!

Android ListView with Adapter Example

How android ListView with adapter works is not so clear to me until I read this awesome article with code examples of Lucas Rocha: Performance Tips for Android’s ListView.

In relation to that, I was inspired again to make a post about android ListView with Adapter, but this one is much more simple than the previous post: How To Customize Android ListView?

Today I’m going to show you the code I use whenever I wanted the user to select an “item” in an AlertDialog with ListView. If you are looking for an example AlertDialog with simple ListView (without an adapter), here’s a blog post I made: Android AlertDialog with ListView.

A ListView item here contains a text (item name) and an ID (item ID), so whenever the user selects an item in the pop up, you’ll be able to get the text and ID of that selected item.
This is very useful if the items are from a database – records with IDs and names. The tags (item IDs) were set using the TextView’s setTag() method.

Video Demo

Here’s the final output of our code for today. Name and ID were fetched and show via toast.

Video Demo Permalink

DOWNLOAD CODE

Let’s Code!

We only need 6 files to run this sample code successfully.
1. activity_main.xml – our main layout. Our trigger to show the ListView is a button.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/buttonShowPopUp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Show AlertDialog with ListView" />

</RelativeLayout>

2. MainActivity.java – will show the main layout, a button at the center of the screen. Clicking that button will show our awesome ListView.

package com.example.listviewpopupwithadapter;

import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.app.Activity;
import android.app.AlertDialog;

public class MainActivity extends Activity {

    AlertDialog alertDialogStores;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // a button to show the pop up with a list view
        View.OnClickListener handler = new View.OnClickListener(){
            public void onClick(View v) {
                switch (v.getId()) {

                    case R.id.buttonShowPopUp:
                        showPopUp();
                        break;
                }
            }
        };
        
        findViewById(R.id.buttonShowPopUp).setOnClickListener(handler);
        
    }
    
    public void showPopUp(){
        
        // add your items, this can be done programatically
        // your items can be from a database
        ObjectItem[] ObjectItemData = new ObjectItem[20];
        
        ObjectItemData[0] = new ObjectItem(91, "Mercury");
        ObjectItemData[1] = new ObjectItem(92, "Watson");
        ObjectItemData[2] = new ObjectItem(93, "Nissan");
        ObjectItemData[3] = new ObjectItem(94, "Puregold");
        ObjectItemData[4] = new ObjectItem(95, "SM");
        ObjectItemData[5] = new ObjectItem(96, "7 Eleven");
        ObjectItemData[6] = new ObjectItem(97, "Ministop");
        ObjectItemData[7] = new ObjectItem(98, "Fat Chicken");
        ObjectItemData[8] = new ObjectItem(99, "Master Siomai");
        ObjectItemData[9] = new ObjectItem(100, "Mang Inasal");
        ObjectItemData[10] = new ObjectItem(101, "Mercury 2");
        ObjectItemData[11] = new ObjectItem(102, "Watson 2");
        ObjectItemData[12] = new ObjectItem(103, "Nissan 2");
        ObjectItemData[13] = new ObjectItem(104, "Puregold 2");
        ObjectItemData[14] = new ObjectItem(105, "SM 2");
        ObjectItemData[15] = new ObjectItem(106, "7 Eleven 2");
        ObjectItemData[16] = new ObjectItem(107, "Ministop 2");
        ObjectItemData[17] = new ObjectItem(108, "Fat Chicken 2");
        ObjectItemData[18] = new ObjectItem(109, "Master Siomai 2");
        ObjectItemData[19] = new ObjectItem(110, "Mang Inasal 2");
        
        // 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();
        
    }

    
}

3. ObjectItem.java – each item in the list is considered as an object with name and ID.

package com.example.listviewpopupwithadapter;

//another class to handle item's id and name
public class ObjectItem {
    
    public int itemId;
    public String itemName;
    
    // constructor
    public ObjectItem(int itemId, String itemName) {
        this.itemId = itemId;
        this.itemName = itemName;
    }

}

4. ArrayAdapterItem.java – where our ListView performance depends.

package com.example.listviewpopupwithadapter;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

// here's our beautiful adapter
public class ArrayAdapterItem extends ArrayAdapter<ObjectItem> {

    Context mContext;
    int layoutResourceId;
    ObjectItem data[] = null;

    public ArrayAdapterItem(Context mContext, int layoutResourceId, ObjectItem[] data) {

        super(mContext, layoutResourceId, data);
        
        this.layoutResourceId = layoutResourceId;
        this.mContext = mContext;
        this.data = data;
    }

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

        /*
         * 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);
        }
        
        // object item based on the position
        ObjectItem objectItem = data[position];
        
        // get the TextView and then set the text (item name) and tag (item ID) values
        TextView textViewItem = (TextView) convertView.findViewById(R.id.textViewItem);
        textViewItem.setText(objectItem.itemName);
        textViewItem.setTag(objectItem.itemId);

        return convertView;
        
    }
    
}

5. OnItemClickListenerListViewItem.java – answers what will happen if the user clicks an item in the list?

package com.example.listviewpopupwithadapter;

import android.content.Context;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import android.widget.Toast;

/*
 * Here you can control what to do next when the user selects an item
 */
public class OnItemClickListenerListViewItem implements OnItemClickListener {

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        Context context = view.getContext();
        
        TextView textViewItem = ((TextView) view.findViewById(R.id.textViewItem));
        
        // get the clicked item name
        String listItemText = textViewItem.getText().toString();
        
        // get the clicked item ID
        String listItemId = textViewItem.getTag().toString();
        
        // just toast it
        Toast.makeText(context, "Item: " + listItemText + ", Item ID: " + listItemId, Toast.LENGTH_SHORT).show();

        ((MainActivity) context).alertDialogStores.cancel();
        
    }
    
}

6. list_view_row_item.xml – the layout of each item in the list. You can modify this if you want to have an image or subtitle for each item of your ListView.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp" >

    <TextView
        android:id="@+id/textViewItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:text="Item name here..."
        android:textSize="15dp" />

</RelativeLayout>

This is just the first part, the second part will be about the Android ListView performance optimization with the help of Android ViewHolder Design Pattern.

Please share your thoughts about this Android ListView with Adapter Example!