binpress

Create a Fluid ListView with the ViewHolder Pattern

Don’t forget to check out my online course “Your First Android App with Java”

Tutorial Requirements

You must have a Developer IDE (I recommend Eclipse) and the Android SDK installed. Having an intermediate to advanced knowledge of Android and Java is also recommended. Since this tutorial is about the ListView, it can be more applicable to those with a ListView wired to a custom adapter.

The Problem

So you have an application with a ListView and each row has a follows a hierarchy inflated from perhaps an XML layout file. For example, Pocket has a ListView and each row contains an image thumbnail, a title, and a subtitle. Obviously there’s nothing in the Android default row layout that is akin to this. Let’s suppose our ListView has one thousand entries. When your application has a ListView, you must account for at least a thousand items being in that ListView and the user scrolling through them. You will not have the same results if you are testing your application and it has ten entries versus a thousand entries. Scrolling through those one thousand entries is not going to be all that fluid. You may notice some skipped frames or maybe the images are taking time to load. This is due to the fact that for every entry a new View is created. So if you have one thousand items, then one thousand new views are being created. This method is incredibly expensive in terms of memory.

The Solution

Let’s say you can fit 12 items on your phone’s screen. When you scroll downwards by one, Item 1 is no longer on screen. Item 1 gets tossed into the Recycler that is managed in the internals of ListView. Our objective is to take Item 1, change the text or image, and put it back in the ListView as Item 13. A new View was not created, it was simply recycled. All of this work is done in the adapter.

Implement the Solution

Implementing it is actually fairly straightforward. It will make scrolling through a list of one thousand Views analogous to scrolling through 10 Views. First, you need to identify what the hierarchy of your row items are. For this tutorial, let’s presume we have a title, subtitle, and image. Our ViewHolder class would look like this:

  1. static class ViewHolder {
  2.     TextView title;
  3.     TextView subtitle;
  4.     ImageView icon;
  5. }        

A variable for each user interface widget must be created. Then we input the following code in the getView method of our adapter:

  1. public View getView(int position, View convertView, ViewGroup parent) {
  2.         // Avoid unneccessary calls to findViewById() on each row, which is expensive!
  3.         ViewHolder holder;
  4.  
  5.         /*
  6.          * If convertView is not null, we can reuse it directly, no inflation required!
  7.          * We only inflate a new View when the convertView is null.
  8.          */
  9.         if (convertView == null) {
  10.             convertView = inflater.inflate(R.layout.list_item_pocket, null);
  11.  
  12.             // Create a ViewHolder and store references to the two children views
  13.             holder = new ViewHolder();
  14.             holder.title = (TextView) convertView.findViewById(R.id.title);
  15.             holder.subtitle = (TextView) convertView.findViewById(R.id.subtitle);
  16.             holder.icon = (ImageView) convertView.findViewById(R.id.icon);
  17.  
  18.             // The tag can be any Object, this just happens to be the ViewHolder
  19.             convertView.setTag(holder);
  20.         } else {
  21.             // Get the ViewHolder back to get fast access to the TextView
  22.             // and the ImageView.
  23.             holder = (ViewHolder) convertView.getTag();
  24.         }
  25.  
  26.         // Bind that data efficiently!
  27.         holder.title.setText(someTitle[position]);
  28.         holder.subtitle.setText(someSubtitle[position]);
  29.         holder.icon.setImageBitmap((position % 2) == 0 ? bitmap1 : bitmap2);
  30.  
  31.         return convertView;
  32.     }

With this new getView method, you can efficiently populate and scroll through a list view. At first glance, it might not seem as much of a difference, but when comparing the “new view every time” method to the “view holder” method on multiple devices, performance can be increased by two or three times.

This a pattern that you should be using whenever creating a custom adapter.

If you like this tutorial, don’t forget to check out my online course “Your First Android App with Java”

Author: Mohit Deshpande

Scroll to Top