Pages

Thursday 19 May 2011

Advanced Lists: Using class object within ListView

How do I build a ListView to display my custom class items?

One of the most common interface elements used in Android apps is the ListView. This presents to the user a scrollable list of tappable items. The best place to start with this is and Android Developers ListView Tutorial which will cover the basics of creating an activity with the ListView control and displaying a list of items from a string array.

In a real world app you will probably want to build a list with something other than a string array. Commonly you will have a collection of your own type of object, or you may want to display listview items with an icon. I will show you how to use a ListView to display these objects and also handle the tap event.

Let's start with the class object that will consist of a text label, a drawable for the icon and also an index value to track item selection:

CustomListItem.java
package com.example.advancedList; import android.graphics.drawable.Drawable; public class CustomListItem { private int mIndex; private String mLabel; private Drawable mPicture; public CustomListItem(final int index, final String label, final Drawable pic) { mIndex = index; mLabel = label; mPicture = pic; } public int getIndex() { return mIndex; } public String getLabel() { return mLabel; } public Drawable getPicture() { return mPicture; } }
Now that we have our class defined, let's create the layout that will be used to display the individual list item, which is a simple RelativeView with an ImageView for the icon and a TextView for the label:

Create res/layout/list_item.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
  
   <TextView
       android:id="@+id/txtLabel"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content" 
       android:text="List item" />
   <ImageView
       android:id="@+id/txtImgIcon"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" 
       android:src="@null"
       android:layout_toRightOf="@id/txtLabel" />
  
</RelativeLayout>

Hopefully so far everything has been quite straight forward. We now need to define a custom list adapter that extends the BaseAdapter class, this will allow us to fully control the way the ListView is constructed.

Create a class named MyListItemAdapter.java:
package com.example.advancedList;

import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

// Custom list item class for menu items
public class MyListItemAdapter extends BaseAdapter {

    private List<CustomListItem> items;
    private Context context;
    private int numItems = 0;

    public MyListItemAdapter(final List<CustomListItem> items, Context context) {
        this.items = items;
        this.context = context;
        this.numItems = items.size();
    }
 
    public int getCount() {
        return numItems;
    }

    public CustomListItem getItem(int position) {
        return items.get(position);
    }

    public long getItemId(int position) {
        return 0;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
       
        // Get the current list item
        final CustomListItem item = items.get(position);
        // Get the layout for the list item
        final RelativeLayout itemLayout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        // Set the icon as defined in our list item
        ImageView imgIcon = (ImageView) itemLayout.findViewById(R.id.imgIcon);
        imgIcon.setImageDrawable(item.getPicture());
     
        // Set the text label as defined in our list item
        TextView txtLabel = (TextView) itemLayout.findViewById(R.id.txtLabel);
        txtLabel.setText(item.getLabel());

        return itemLayout;
    }
 
}

Next let's knock together a simple layout for the main activity. This will consist of a ListView for our items and a TextView that is displayed if the list is ever empty. Note here the use of special Android reserved IDs for the controls. This allows the list to be picked up for the ListActivity we'll use and also know to only display the TextView if there is no data in the ListView.

We'll call the file res/layout/list_example.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent">

    <ListView
      android:id="@id/android:list"
      android:layout_height="wrap_content"
      android:layout_width="fill_parent" />
     
   <TextView
      android:id="@id/android:empty"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Nothing to list" />
</LinearLayout>

We now need to build our main Activity which extends the ListActivity class using our layout defined previously. Within the activity's onCreate method we create a List of our CustomListItem type and add 3 new items to it. We then populate a new ListAdapter using our myListItemAdapter class and assign to it our list of items:

src/com.example.advancedList/Main.java :
package com.example.advancedList;

import java.util.ArrayList;
import java.util.List;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;

public class Main extends ListActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_example);
        
// Create list of items based upon class
        final List<CustomListItem> items = new ArrayList<CustomListItem>(3);
     
        items.add(new CustomListItem (1, "Item 1", getResources().getDrawable(R.drawable.icon)));
        items.add(new CustomListItem (2, "Item 2", getResources().getDrawable(R.drawable.icon)));
        items.add(new CustomListItem (3, "Item 3", getResources().getDrawable(R.drawable.icon)));
     
        // Populate the list view
        ListAdapter adapter = new myListItemAdapter(items, this);
        ListView lv = getListView();  // Because we are extending ListActivity we cal  this but could specify
        lv.setAdapter(adapter);
        lv.setTextFilterEnabled(true);
     
        // Add listener for item clicks
        lv.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
             
                  // Retrieve our class object and use index to resolve item tapped
                    final MenuListItem item = items.get(position);
                    final int menuIndex = item.getIndex();
                    switch (menuIndex) {
                    case 1:                      
                        break;
                    case 2:                      
                        break;
                    case 3:
                        break;
                    default:
                        break;
                    }
            }
        });      
    }
 
}

You'll see that once we have our list adapter set up it is applied to the list view. We then assign a listener to the list view to pick up click events. Within this the first thing we do it obtain the item from the list that was clicked. We then have the freedom to make our code react to any aspect of our object.

I have used this approach several times and in fact have never displayed a list from a string array. I hope you find this guide useful.


4 comments:

Anonymous said...

thx it helps me...

Anonymous said...

thank you
have been looking all day for something like trhat, thisone works out of the box.
Just 2 naming issues:
CustomListItem becomes MenuListItem
and
txtImgIcon becomes imgIcon
otherwise it works thank you !!!

Anonymous said...

Custom ListView control

Anonymous said...

ListView item class object

Post a Comment