Sunday, October 17, 2010

Fancy List Items in a ListView

A while ago, I was working on a project where I wanted to be able to have Android show a ListView, with custom entries in the list. In most other languages and toolkits, doing this is a bit of a pain. You have to implement a special painter to pain the list cell in just the right way. Unless you are a pretty die-hard developer for that toolkit you may find yourself horribly confused as to how to handle the situation.

Fortunately, Android made this REALLY easy. If you know how to do a layout for a form, you know almost everything you need to know in order to make a fancy list item.

First, you want to create a small layout that specifies how a single list entry should look. For my purposes, I wanted to have a single line of large text, with smaller text below. I also wanted the large text to be bright white, and the smaller text to be a darker gray color. Finally, the smaller text should be indented on both sides to indicate that it is subordinate to the larger text.

No problem! A basic linear layout will do the job nicely. My final layout looks like this :


<linearlayout android="http://schemas.android.com/apk/res/android" layout_height="wrap_content" layout_width="fill_parent" orientation="vertical">
<textview layout_height="wrap_content" id="@+id/LargeText" text="Large Text" textcolor="@color/white" textsize="25dip" layout_width="fill_parent"></textview>
<textview layout_height="wrap_content" layout_width="fill_parent" layout_marginright="10dip" layout_marginleft="10dip" id="@+id/SmallText" text="Smaller Text" textcolor="#c0c0c0"></textview>
</linearlayout>


Easy enough. Now that we have that, we need to actually write some code that will use it. I find it easiest to create a convenience class that lets me manipulate the list easily. So, I create a class called MultiLineList, that looks something like this :


import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.widget.SimpleAdapter;

public class MultiLineList {

private ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
private Activity parentActivity;
private SimpleAdapter taskAdapter;

public MultiLineList(Activity parent)
{
parentActivity = parent;

taskAdapter = new SimpleAdapter(parentActivity, list,
R.layout.multi_item_layout, new String[] { "largetext","smalltext" },
new int[] { R.id.LargeText, R.id.SmallText } );
}

public void clear()
{
list.clear();
}

public void addItem(String largeText, String smallText)
{
HashMap<String,String> item = new HashMap<String,String>();
item.put("largetext", largeText);
item.put("smalltext", smallText);
list.add(item);
}

public SimpleAdapter getData()
{
return taskAdapter;
}
}


In the constructor, we create a SimpleAdapter that we use to map our text in to the different rows of the list view we will show the user. "R.layout.multi_item_layout" is the name of the layout we created above. (You may need to change this if you called your layout something else.) The two strings, followed by the two ints give us a mapping in a hash table for how to identify the two different TextEdits that we put in the layout. There is nothing special about only having two items. We could easily have 10 strings and 10 matching ints. What is important is that each string has a corresponding int that maps to a text edit in your layout.

The addItem() method is where the good stuff happens. As part of creating the SimpleAdapter in the constructor, we told it to use the "list" variable to get its data out of. So, the addItem() method allows us to easily put new text in to the list to be used. So, calling addItem("I am big", "I am not") would give us a single entry in the list view that contained the text "I am big" in the large font, and "I am not" in the small font.

Now that we have all of the ground work in place, it is time to put the pieces together. Moving forward, I am going to assume that you have a basic form set up with a list view configured in it. Also, that a ListView variable points to it called "listView". From there, using our fancy list widgets would look something like this :


myList = new MultiLineList(this);
myList.addItem("Large 1", "Small 1");
myList.addItem("Large 2", "Small 2");

listView.setAdapter(myList.getData());



Now, the next time out list view is drawn, it will contain two items in the list. The first one will have "Large 1" in the larger text, and "Small 1" in the smaller text. The second one will contain "Large 2" in the larger text, and "Small 2" in the smaller text.

From this simple example, you should be able to easily expand your list items to contain any just about any widget type you would want. But, I leave that up to you to experiment with.