Android tutorial – List Views with View Holder Pattern

Usually when you write apps for android you will need a list of some sort. That list items will need to have a custom layout and for that we will use a xml layout and an adapter for our listview. The items layout will be set using a viewholder which will be inside your adapter class, so lets see an example of that:

class MyAdapter extends BaseAdapter
{
/**
* The inflater.
*/
protected LayoutInflater inflater;

/**
* The items list. Here you can set any type, I used String because I was interested only in passing a string, but you get the idea
*/
protected List<String> items;

@Override
public int getCount() {
return items.size();
}

@Override
public Object getItem(int i) {
return items.get(i);
}

@Override
public long getItemId(int i) {
return i;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
//we will populate this method later on
return convertView;
}
//in ViewHolder we will keep the layout for the listview items, so if we need to add an imageview to our items, we will add a new ImageViwe variable in the ViewHolder class
private class ViewHolder
{
TextView Title;
TextView SubTitle;
}

Now we need to create a layout for our listview items in listview_custom_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:gravity="center"
    android:orientation="vertical" >
        <TextView
            android:id="@+id/listview_adapter_title_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:text="Some great words"
            android:textStyle="bold"/>
        <TextView
            android:id="@+id/listview_adapter_subtitle_textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:text="Some not so great words"
            android:textStyle="italic"/>
</LinearLayout>

To optimize our code we will reuse the items view. Let’s say on our screen can fit a listview with 10 items at a time. When we scroll the list a little bit there will be 11 items on the screen (9 full items, 1/2 of the first item and 1/2 of the last item). That means that always we will have a maximum of 11 items on the screen. When we scroll a little more, the first item is hidden so instead of allocating a new memory block for a new ViewHolder object to show the 12th item, we will recycle the ViewHolder of the first item and change its values.

Summary 1

Summary 2

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

//if it is a new item
if (convertView == null) {
convertView = inflater.inflate(R.layout.listview_custom_item, parent, false);

holder = new ViewHolder();

holder.theTitleTextView = (TextView) convertView.findViewById(R.id.listview_adapter_title_textview);
holder.theSubtitleTextView = (TextView) convertView.findViewById(R.id.listview_adapter_subtitle_textview);

convertView.setTag(holder);
} else {
//here we recycle the previous ViewHolder, by using an older one
holder = (ViewHolder) convertView.getTag();
}

//make sure you set the new values after creating or reusing the ViewHolder, otherwise if you are setting these when creating a new ViewHolder then when you are scrolling, the items will not refresh
holder.theTitleTextView.setText(items.get(position));
holder.theSubtitleTextView.setText("Item #" + position);

return convertView;
}

One thing to consider would be that if you set a property for an element, then you have to reset that property for every other element.
Let’s consider a view holder with a TextView and an ImageView. If you set your ImageView to be visible when the TextView’s text is longer than 5 characters, then you will have to set the ImageView to be invisible when the TextView’s text is shorter then 5 characters. If you do not set the ImageView to become invisible when the text is shorter than 5 characters then the ImageView will still be visible from a previous viewholder. So basically when we use a previous viewholder we must reset all of its elements.

Bad code:

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

		//init viewholder item here

		if(holder.textView.getText().length > 5)
		{
			holder.imageView.setVisibility(true);
		}
	}

Good code:

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

		//init viewholder item here

		if(holder.textView.getText().length > 5)
		{
			holder.imageView.setVisibility(true);
		}
		else {
			holder.imageView.setVisibility(false);
		}
	}

Leave a Reply

You must be logged in to post a comment.