Multicolumn ListView in Android

Ever since I started programming on the Android platform, I have been wondering when the SDK would include a ready-made multicolumn ListView (or listbox as it is often called in other frameworks). One could of course construct such a thing by slapping regular ListViews side by side and then painfully adding code to keep them all in sync when scrolled and so on. It felt such a hack that I used GridView instead. GridView is not really a great list, though, because the cells will all be formatted the same. There are other issues besides that, but for me that was the main one.

I finally saw a blog post which customized ListView to put two TextViews vertically into one line. It was pretty simple going from there to one that would display three TextViews side-by-side. The main layout XML file could define ListView something like this:

<!-- main.xml -->
<ListView android:id="@+id/SCHEDULE" android:layout_width="wrap_content" android:layout_height="wrap_content">
</ListView>

Here is the XML for each row, main point being that we put three TextViews in LinearLayout with horizontal orientation:

<?xml version="1.0" encoding="utf-8"?>
<!-- row.xml -->
<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:paddingTop="4dip"
     android:paddingBottom="6dip"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal">
 
     <TextView android:id="@+id/TRAIN_CELL"
         android:layout_width="50dip"
         android:layout_height="wrap_content"/>
 
     <TextView android:id="@+id/FROM_CELL"
         android:layout_width="70dip"
         android:layout_height="wrap_content" android:layout_weight="1"/>
 
     <TextView android:id="@+id/TO_CELL"
         android:layout_width="60dip"
         android:layout_height="wrap_content"  android:layout_weight="1"/>
</LinearLayout>

Notice how you can format each TextView separately. There is no column separator, but something decent should not be too hard to whip up if desired. My understanding is that the screen width is supposed to be 160 dip, but for some reason I had to use width values that actually add up to more than that, as well as using layout weight to grow some fields proportionally so that when you switch to landscape mode things are still nicely aligned. I would have expected specifying widths in dip would have automatically done that.

Here is how you could populate that ListView with data:

ListView list = (ListView) findViewById(R.id.SCHEDULE);
 
ArrayList<HashMap<String, String>> mylist = new ArrayList<HashMap<String, String>>();
HashMap<String, String> map = new HashMap<String, String>();
map.put("train", "101");
map.put("from", "6:30 AM");
map.put("to", "7:40 AM");
mylist.add(map);
map = new HashMap<String, String>();
map.put("train", "103(x)");
map.put("from", "6:35 AM");
map.put("to", "7:45 AM");
mylist.add(map);
// ...
mSchedule = new SimpleAdapter(this, mylist, R.layout.row,
            new String[] {"train", "from", "to"}, new int[] {R.id.TRAIN_CELL, R.id.FROM_CELL, R.id.TO_CELL});
list.setAdapter(mSchedule);

The main point here is that the SimpleAdapter requires the data to be in a List, where each entry is a Map. In my case, I simulate the columns by putting in three entries into each of the maps, and the maps each have the same keys. The adapter then maps the key values (like "train") to the corresponding TextView (R.id.TRAIN_CELL).

Putting the above pieces of code into Caltroid produces results that look like this:

Multicolumn ListView in Android

Multicolumn ListView in Android

There is quite a bit of work to create the data structure for the adapter in this way, so for anything involving more than a trivial amount of data this is probably going to be too slow. With more data, a database is almost certainly involved and the SimpleCursorAdapter works almost exactly like SimpleAdapter, except that SimpleCursorAdapter maps column names from the query results to the appropriate views.

Similar Posts:

    None Found

9 Comments

  1. Sikus:

    Hi, Can I get to a component in the code from XML? I do 3 TextView and 2 ImageView, but I need different images give to the background ImageView in the code. I can not use setContentView() because it destroi my layout. Can anybody help me?
    P.S.: Sorry, my english is very poor :-)

  2. Heikki Toivonen:

    I don’t know. Your best bet would be to ask on the android developer discussion groups and forums.

  3. Sikus:

    So, I have it, I fix my problem, juhu :-) ):

    map = new HashMap();
    map.put(“train”, “ProWide media s.r.o”);
    map.put(“from”, “pokus”);
    map.put(“gif”, R.drawable.some_picture_png + “”);
    mylist.add(map);

    setListAdapter(new SimpleAdapter(this, mylist, R.layout.main,
    new String[] {“train”, “from”, “gif”}, new int[] {R.id.TextView01, R.id.TextView02, R.id.ImageView}));

  4. LK:

    Hello! I am a Chinese developer, I am pleased to see your article, but I have a question, how in the list show different icons?

  5. GBaryiames:

    Great article here.

    http://www.softwarepassion.com/android-series-custom-listview-items-and-adapters/

    It explains how to create a custom Adapter, that can be used to populate your multi-column list view without having to go through the extra work of building up a map. The custom Adapter is more efficient.

  6. PLIACHAS PASCHALIS:

    how could you do this by using gridView instead of ListView?

  7. Heikki Toivonen:

    @PLIACHAS: It wasn’t pretty, but I just put the data into an ArrayAdapter and set that as the GridView adapter. Same info as in the list: train number, from and to times. You can look at the code in http://svn.heikkitoivonen.net/caltroid/trunk/src/net/heikkitoivonen/android/caltroid/Caltroid.java (that’s the Open Source version of Caltroid that is no longer maintained).

  8. Stephen:

    Can you apply an OnItemSelected listener to any of these rows?

  9. Stephen:

    woops, yes you can. I just wasn’t clicking on the right part :-)