The ViewHolder Pattern

As you develop your Android application, performance becomes more and more important. If your application appears slow, the user will get frustrated and not want to use it. One example of where this could happen is in the scrolling of a ListView. ListViews are a popular way to display information and can be done very poorly.

Each ListView has an adapter that is responsible for displaying the information in the list, and many times we’ll want to write a custom adapter to do something special. Within that adapter, the getView() method is responsible for figuring out how to display each cell in the list.  There are two main ways you’ll see developers implement this method–the right way and the wrong way.

The wrong way:

public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater mInflater = mContext.getLayoutInflater();
    View convertView = mInflater.inflate(R.layout.buddy_list_item, null);
    Buddy buddy = getItem(position);

    ((TextView) convertView.findViewById(R.id.buddy_full_name)).setText(buddy.getFullName());
    ((TextView) convertView.findViewById(R.id.buddy_username)).setText(data.getUsername());
    return convertView;
}

In this example, each time a cell needs to be created, the ListView will need to find the View and its components (see where findViewById() is called?). This means the code will scan over all the layout resources and find this View and get its components to then populate in the cell. This is very expensive, and could lead to poor performance.

The correct way:

public static class ViewHolder {
    TextView fullName;
    TextView username;
}

Now, we use a ViewHolder which will represent a cell in our ListView. The ViewHolder will be attached to the tag field of our View. This will make it much easier for the ListView to recall the correct View and components, rather than calling findViewById() many times. Let’s see how it’s used in the getView() method.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView==null) {
        holder = new ViewHolder();
        convertView = mInflater.inflate(R.layout.list_item_buddy, null);
        holder.fullName = (TextView)convertView.findViewById(R.id.buddy_full_name);
        holder.username = (TextView)convertView.findViewById(R.id.buddy_username);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    final Buddy buddy = buddies.get(position);
    holder.fullName.setText(buddy.getFullName());
    holder.username.setText(buddy.getUsername());
    return convertView;
}

Here, we only inflate a View from scratch if convertView is null. Otherwise we have a reference to the View through the convertView’s tag, and we don’t need to call findViewById at all! This leads to great performance and smoother scrolling in our list.

Hopefully it’s clear why the ViewHolder pattern is superior. There are some great Android docs that touch on this. If you need clarity or have questions or suggestions, be sure to comment below!

Happy coding!
Brandon from The Bunch

Leave a Reply

Your email address will not be published. Required fields are marked *