Mobile Zone is brought to you in partnership with:

Justin is responsible for designing prototypes and proofs of concepts for in-house projects, some of which include using the APIs of the Android operating system. He's also experienced with MuleSoft support, training materials creation and testing of Cloud Connectors for Mule. Here's his full bio Justin is a DZone MVB and is not an employee of DZone and has posted 3 posts at DZone. You can read more from them at their website. View Full User Profile

Optimizing Your ListView with the ViewHolder Pattern

10.03.2013
| 7330 views |
  • submit to reddit

The ListView is a widget used extensively in Android applications to display data in a structured fashion. It is also a view that can be quite tricky to optimize, and making your applications feel less jittery is extremely important. When developing an application, a developer has to keep in mind the wide range of hardware on which Android runs. Nowadays most high end phones and tablets are equipped with at least a dual core processor, which can handle unoptimized code at tolerable speeds. However, a large percentage of Android devices are still using single core processors with limited memory. This blog aims to deliver one form of optimization that developers can place in their code at next to no effort.

The ViewHolder pattern is a pattern that a developer can use to increase the speed at which their ListView renders data. The reason for this improvement is that the number of times which the findViewById method is invoked is drastically reduced, existing views do not have to be garbage collected and new views do not have to be inflated. A typical ListView adapter contains the following method signature:

public View getView (int position, View convertView, ViewGroup parent)

The ‘position’ integer stores the position of the current item within the adapter’s data set. The ‘convertView’ field is a reference to the old view that we are planning to reuse. This is the field that we use to implement our optimisation. It is extremely important to use this variable. Not doing so eventually leads to an OutOfMemoryException. This happens because internally a ListView keeps a reference to views it has already seen. Not reusing the ‘convertView’ field will continuously add new views to the ListView, causing a noticeable slowdown of your application and eventually lead to your application crashing. The ‘parent’ field is a reference to the parent view that this view is eventually attached to. Consider the following class:

public class Person {
    private String name;
    private String surname;
    private Bitmap image;
   
    public Person(String name, String surname) {
        this.name = name;
        this.surname = surname;
    }

    public String getName() {
        return name;
    }

    public String getSurname() {
        return surname;
    }

    public Bitmap getImage() {
        return image;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public void setImage(Bitmap image) {
        this.image = image;
    }
}

Now we want to print a list of persons, all of whom are paired with an image. We need one ImageView for the image, and two TextViews for the name and surname fields. Our ViewHolder class would look something like the following and reside as a static inner class inside our Activity (or Fragment):

static class ViewHolder {
    private TextView nameTextView;
    private TextView surnameTextView;
    private ImageView personImageView;
}

We then implement our own ArrayAdapter for our list of persons that we want to display:

private class PersonsAdapter extends ArrayAdapter<Person> {

    ...

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
       
        ViewHolder holder;
       
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_entry, null);
            holder = new ViewHolder();
            holder.nameTextView = (TextView) convertView.findViewById(R.id.person_name);
            holder.surnameTextView = (TextView) convertView.findViewById(R.id.person_surname);
            holder.personImageView = (ImageView) convertView.findViewById(R.id.person_image);
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }
           
        Person person = getItem(position);
           
        holder.nameTextView.setText(person.getName());
        holder.surnameTextView.setText(person.getSurname());
        //holder.personImageView.setImageBitmap(person.getImage());

        return convertView;
    }
}

Note that I have purposefully commented out the loading of bitmaps in the ListView. This is something that has given a lot of trouble to developers and might be something we could address in a future blog post. Given the relatively small heap size that Android applications are allowed to access, developers need to take several steps to display multiple images on screen in an efficient manner. Downsampling of images is just one example of many possible optimizations.


Published at DZone with permission of Justin Saliba, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)