Making Custom Rows in Android ListViews
Hi all,
I’ve been spending the last little while writing software for Google’s Android mobile phone platform and I’ve struggled with a few things along the way. I thought it might be a good idea to write about some of those struggles and their eventual solutions. This particular one held me up for several days and, try as I might, an example or solution was nowhere to be found.
In Android you can bind a sqlite database curser directly to a ListView on the display using a furnished adapter called SimpleCursorAdapter. While this is great it makes a pretty boring ListView by default. Each row is exactly the same. I wanted to change the text colour on some rows depending on a field from the database that I wasn’t actually going to display. First lets have a look at the layouts:
/res/layout/pod_view.xml
<?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">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title"
android:paddingTop="4pt" />
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/font_size_for_pod_row"
android:paddingBottom="6pt" />
<ListView android:id="@+id/android:list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Nothing to exotic here, just a ListView with a title at the top. Notice that the layout_width is set to “fill_parent” on both the outer container, LinearLayout, and the ListView itself. Without this users will only be able to click the part of each row to select it. If the text doesn’t go all the way across the row it creates an unclickable dead spot which is a bit confusing.
Now each row also has a layout like this one
/res/layout/pod_row.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/text1" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/font_size_for_pod_row"
android:paddingTop="@dimen/vertical_padding_for_pod_row"
android:paddingBottom="@dimen/vertical_padding_for_pod_row"/>
Again mind the layout_width attributte. I’ve stored the values for padding and text size in another spot to make adjustments easy. For the sake of completeness…
/res/values/dimen.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="font_size_for_pod_row">20sp</dimen>
<dimen name="vertical_padding_for_pod_row">6pt</dimen>
<dimen name="font_size_for_show_row">14sp</dimen>
<dimen name="vertical_padding_for_show_row">3pt</dimen>
</resources>
And that’s how you might do that. This is handy if you have values you want to reuse and tweak in different layouts throughout your project.
Phwew, done with layouts.
Lets dive in and have a look at the modifications you can make to SimpleCursorAdapter. I’m not going to talk about setting up a basic SimpleCursorAdapter because there are tonnes of examples out there. One of them is in the official android tutorial. Here is the function that draws my screen within my Activity:
private void populateFields() {
// This will be used by our SimpleCursorAdapter to bind fields in each row to
// data from our cursor. Note that we have an extra field in the cursor that
// determines a display attribute for the field we display.
class ShowViewBinder implements SimpleCursorAdapter.ViewBinder {
// this cursor is built by calling a function that returns a few things.
// right now I'm interested in the first (title) and the third
//(downloaded status)
private static final int DATA_COLUMN = 1;
private static final int STATUS_COLUMN = 3;
boolean retval = false;
//@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if ( columnIndex == DATA_COLUMN) {
int status = cursor.getInt(STATUS_COLUMN);
TextView tv = (TextView) view;
switch ( status ) {
case PodDbAdapter.DOWNLOADED:
tv.setTextColor(Color.GREEN);
tv.setText(cursor.getString(DATA_COLUMN));
retval = true;
break;
case PodDbAdapter.TO_DOWNLOAD:
tv.setTextColor(Color.rgb(0xFF, 0x99, 0));
tv.setText(cursor.getString(DATA_COLUMN));
retval = true;
break;
case PodDbAdapter.DOWNLOAD_ERROR:
tv.setTextColor(Color.RED);
tv.setText(cursor.getString(DATA_COLUMN));
retval = true;
break;
default:
tv.setTextColor(Color.WHITE);
tv.setText(cursor.getString(DATA_COLUMN));
retval = true;
break;
}
}
return retval;
}
}
Cursor pod = mDbHelper.fetchPod(mRowId);
startManagingCursor(pod);
mTitleText.setText(pod.getString(pod.getColumnIndexOrThrow(PodDbAdapter.KEY_TITLE)));
Cursor showsCursor = mDbHelper.fetchShows(mRowId);
startManagingCursor(showsCursor);
// Create an array to specify the fields we want to send into our ViewBinder
String[] from = new String[]{PodDbAdapter.SHOW_KEY_TITLE, PodDbAdapter.SHOW_KEY_STATUS};
// and an array of the fields we want to bind those fields to (in this case just text1)
int[] to = new int[]{R.id.text1};
// Now create a simple cursor adapter and set it to display
SimpleCursorAdapter shows = new SimpleCursorAdapter(this, R.layout.show_row_white, showsCursor, from, to);
shows.setViewBinder(new ShowViewBinder());
setListAdapter(shows);
}
There we have it. Neat huh?
What I’ve done here is implimented a subclass of SimpleCursorAdapter that was furnished for just such an occasion: SimpleCursorAdapter.ViewBinder. My Cursor returns an extra field that we won’t display. We’ll use that to determine the text colour of the data that is displayed by pulling that field out of the Cursor and having a look. There are two Cursors in play in my example. One of them is being used to populate the TextView in my pod_view.xml layout but the second is where the magic happens. You can see where my ViewBinder is attached down near the bottom with shows.setViewBinder(new ShowViewBinder());
Lastly here’s the result.

And there we have it.. Looks like I have a couple of podcasts to listen to
-Tyson
Tags: Android, dimen, ListView, SimpleCursorAdapter, SimpleCursorAdapter.ViewBinder, ViewBinder







Excuse the ignorance , but how would you call this from your ListActivity ?
Avron,
In my case this was a function that was in my ListActivity Class. I called it from within onCreate(). You could paste this guy right into your onCreate() and remove the wrapping function definition if you felt like it but I separated it for tidyness.
If you need any clarification let me know
-Tysoin
Hey mate,
many thanx for the help you provide through this tutorial. The only problem I faced when trying to
follow your way is that as I am using is inside a ListActivity, when I try to scroll down, the application crashes.:(
If you have any idea what might cause this, please let me know.
Many thanx in advance!
Hi there,
Without seeing the stack trace I’m not sure what might be going on there. You can see it either from eclipse or by using ‘adb logcat’. Myabe it’ll have some clues. Post if it you’re still having trouble.
-Tyson
iserm,
Tyson is right. Without an actual stack trace it would be hard to debug. However, I did have a similar problem when scrolling / switching to another activity.
I found that this had to do with the onDestroy method for the activity. There can sometimes be objects that you haven’t yet initialized in your class which are “destroyed” in the onDestroy (or onDraw, onCreate, etc) and which cause a nullpointerException. Most languages throw a fit if you try to perform any action on an object which is still set to null.
Thanks, found this very useful
Nice tutorial, thanks for taking the time to write it up and share.
Hi there, I found this example while looking for a standard SimpleCursorAdapter example, your blog post is one of the highest returning google results on the subject. This looks interesting, but what I’m actually interested in right now is one of those “tons of other basic examples”. Unfortunately I can’t find any!
It would be really helpful to myself (or perhaps future people running into the same thing) if you linked to one of those aforementioned tons of examples
Thanks for this tutorial .
sir , i am new to android . please send full source of this application .
Thank you sir.
Thank you your example was very helpful!
If I could ask something further though: How would you expand this to generate coloured, 2 line rows?
I’m not sure how to have the 2 rows of text binded to the single View in setViewValue
Hi can you help me out with this?
This is the example from the android developer website, HelloListView.
I’m having some problems over here ->
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent, View view, int position, long id)
{
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
Toast.LENGTH_SHORT).show();
}
});
The onItemClickListener and View view having problems. Can you help me out? Thanks!
This is a good tutorial, and I basically implemented the same technique with good success.
Now the second part is: If we not only want to display data from the database, but we also want to have checkboxes, radio buttons, edit text fields as part of this list to allow changes to this data, what tricks do we need to do this?
Would be nice to see a second part of this tutorial to review those techniques, since there are some tricks to doing it and it differs from using the techniques with ArrayAdapters.
Good tutorial!
And i agree on Paul’s comment: a second part would be very helpful-