Android ListView with ViewHolder Tutorial

In this tutorial I will show you how to create a simple ListView with a custom ListAdapter. But first, let’s see why to use ListView. Why shouldn’t we use ScrollView instead, regarding that it’s easier to create? Well your questions are good ones 🙂 .
The advantage of the ListView is that it uses an Adapter which have the ability to use the same view and not to create thousands of objects, like a ScrollView would, if the amount of data is very big. The Adapter has a complex mechanism for recycling the view. But I will explain more within this tutorial. So let’s begin! 🙂
1. Create a new project called “ListViewExample” and the name your main activity to “MyActivity”
2. Now go to res – layout – main.xml  and create in the main.xml the ListView like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
        >
    <ListView android:id="@+id/list"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:transcriptMode="alwaysScroll"
              android:cacheColorHint="#00000000"
              android:listSelector="@android:color/transparent"/>
</LinearLayout>

3. Now that we have the ListView, we have to create the item which will be added to the list. In res – layout create a new xml file and call it list_item.xml. Here we will put a TextView because our list will be a list of names.

<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/list_item"
        android:gravity="center_vertical">
 
 
    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:id="@+id/list_item_text_view"
              android:textSize="20sp"
              android:padding="10dp"
              android:layout_marginLeft="5dp"/>
 
</LinearLayout>

4. Now we have to create the Adapter, the one that we spoke about earlier. So create a new class and call it “MyCustomAdapter” and put the code from below:

package com.example;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;


public class MyCustomAdapter extends BaseAdapter {
    private ArrayList<String> mListItems;
    private LayoutInflater mLayoutInflater;

    public MyCustomAdapter(Context context, ArrayList<String> arrayList){

        mListItems = arrayList;

        //get the layout inflater
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        //getCount() represents how many items are in the list
        return mListItems.size();
    }

    @Override
    //get the data of an item from a specific position
    //i represents the position of the item in the list
    public Object getItem(int i) {
        return null;
    }

    @Override
    //get the position id of the item from the list
    public long getItemId(int i) {
        return 0;
    }

    @Override

    public View getView(int position, View view, ViewGroup viewGroup) {

        // create a ViewHolder reference
        ViewHolder holder;

        //check to see if the reused view is null or not, if is not null then reuse it
        if (view == null) {
            holder = new ViewHolder();

            view = mLayoutInflater.inflate(R.layout.list_item, viewGroup, false);

            // get all views you need to handle from the cell and save them in the view holder
            holder.itemName = (TextView) view.findViewById(R.id.list_item_text_view);

            // save the view holder on the cell view to get it back latter
            view.setTag(holder);
        } else {
            // the getTag returns the viewHolder object set as a tag to the view
            holder = (ViewHolder)view.getTag();
        }

        //get the string item from the position "position" from array list to put it on the TextView
        String stringItem = mListItems.get(position);
        if (stringItem != null) {
            //set the item name on the TextView
            holder.itemName.setText(stringItem);
        } else {
            // make sure that when you have an if statement that alters the UI, you always have an else that sets a default value back, otherwise you will find that the recycled items will have the same UI changes
            holder.itemName.setText("Unknown");   
        }

        //this method must return the view corresponding to the data at the specified position.
        return view;

    }

    /**
     * Used to avoid calling "findViewById" every time the getView() method is called,
     * because this can impact to your application performance when your list is large  
     */
    private class ViewHolder {

        protected TextView itemName;

    }
}

Let’s explain some methods from this class. Our adapter “MyCustomAdapter” extends the BaseAdapter class which will force you to implement the following methods: getCount(), getItem(), getItemId() and getView(). In this tutorial we will modify only the first and the last method: getCount(), respectively getView(). 


getCount(): this method represents how many items are in the list. It returns the number of the items.

getView(): this method is the most important. Here we handle the cells views. This method is called when the ListView wants to display an item on the screen and not less than getCount(). For example we have 20 items, so getCount() will return the number 20 and only 3 items can be visible at a time on the ListView because there is no more room then, getView will get called at first only 3-4 times and if the user starts to scroll the list, getView is called again all the time the ListView needs to display a new item on the screen.

ViewHolder class: ViewHolder is a pattern used to avoid calling “findViewById” every time the getView() method is called. Imagine that you have a list with hundreds of rows, the “findViewById” will be called hundreds of times and it’s not necessary, just bad for performance. So to increase the performance try to use the ViewHolder pattern as much as possible.

5. Now that we created the custom adapter we can go to MyActivity class and create the ListView object with it’s new adapter and show it on the screen 🙂

package com.example;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

import java.util.ArrayList;

public class MyActivity extends Activity
{
    private ListView myList;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        ArrayList<String> list = new ArrayList();

        //for simplicity we will add the same name for 20 times to populate the list view
        for (int i = 0; i < 20; i++){
            list.add("Diana" + i);
        }

        //relate the listView from java to the one created in xml
        myList = (ListView) findViewById(R.id.list);

        //show the ListView on the screen
        // The adapter MyCustomAdapter is responsible for maintaining the data backing this list and for producing
        // a view to represent an item in that data set.
        myList.setAdapter(new MyCustomAdapter(MyActivity.this, list));


    }
}

Now lets’s the result of our work!

 

keyboard_arrow_up