Tuesday, May 13, 2008

Expandable lists

Multilevel, expandable lists are useful user interface elements and Android does support two-level expandable lists. This fact would not be worth a blog entry if there were no traps using the widget.

Click here to download the example program.

The example program implements a simple, two-level expandable list to display color shade information.



The first thing to note is that our Activity extends android.app.ExpandableListActivity. Otherwise setting up the list is pretty similar to ordinary ListActivity classes. The tricky part is the android.widget.SimpleExpandableListAdapter.

SimpleExpandableListAdapter expListAdapter =
new SimpleExpandableListAdapter(

this,

createGroupList(), // groupData describes the first-level entries

R.layout.child_row, // Layout for the first-level entries
new
String[] { "colorName" }, // Key in the groupData maps to display

new int[] { R.id.childname }, // Data under "colorName" key goes into this TextView
createChildList(), // childData describes second-level entries
R.layout.child_row, // Layout for second-level entries

new String[] { "shadeName", "rgb" }, // Keys in childData maps to display

new int[] { R.id.childname, R.id.rgb } // Data under the keys above go into these TextViews

);


First of all, this adapter requires somewhat complicated (although pretty well-documented) data structures. There are two of them, one describing the first-level group elements, the other describing the second-level string elements.

Here is the first one:
List->Map
Every map describes one first-level group element. The keys in the Map are arbitrary but are specified for SimpleExpandableListAdapter so that it can map the keys in the Map to widget IDs. In our case, the data under key name "colourName" will be set as the value of the TextView with "childname" ID.

The second one is a bit more complicated.
List->List->Map
Every entry in the first List represents a group. Entries in the second List represent entries of the group and each such entry is a Map that describes one child, similarly to the description of groups. In our case, the child Map contains two entries (keys "shadeName" and "rgb") that go to TextViews ("childname" and "rgb").

So far so good. Unfortunately, SimpleExpandableListAdapter has its tricks. First of all, the adapter takes the description of one row from a layout (child_row in our case, located under res/layout/child_row.xml). When the adapter draws the expand button onto the row, it does not consider the content of the row, it simply draws the button over the row. That's the reason child_row.xml defines generous left padding for the first TextView. While this behaviour can be considered just an oddity, the fact that SimpleExpandableListAdapter must be created with identical views for first- and second-level lines is a plain bug that existed at least since m3-rc37a. Having group- and child rows with different styles is a cool and useful feature that is supported by SimpleExpandableListAdapter but does not work.

Replace this fragment:

R.layout.child_row, // Layout for the first-level entries
new String[] { "colorName" }, // Key in the groupData maps to display

new int[] { R.id.childname }, // Data under "colorName" key goes into this TextView


with this one:

R.layout.group_row, // Layout for the first-level entries
new String[] { "colorName" }, // Key in the groupData maps to display

new int[] { R.id.groupname }, // Data under "colorName" key goes into this TextView


and you will see, how the expandable list gets confused.

26 comments:

Vasko said...

You sure helped me a lot with this example ;)
I think that you could use the group_row layout for the first-level elements but when I test it it acts weird ..

anddev said...

I just wanted to know whether you have implemented a customised SimpleExpandedListAdapter.If yes,please do share that as well

Thanks

J.P. said...

This example was excellent and helpful.

The next step would be handling a click event on the child items.

Ed Colmar said...
This comment has been removed by the author.
Vasko said...

Ed it should be like you say (at least one would expect it) but replacing group stuff with children stuff actually makes the code work. Otherwise it doesn't..
Gabor, I haven't worked with Android since I posted here .. If I work on something I will let you know

Rudi said...

Thank you for this example! It is really tricky ... i think "Simple" is the wrong name for this adapter, your tutorial helped me out of some trouble.

Jetti said...

Hi! to all
Please help me, how to apply the animations to all subtypes of views and lists.
Thanx in advance

Gabor Paller said...

Jetti, does this help?

Jetti said...

Thanx Gabor Paller!
Today i will try this.

Bicou said...

Thank you very much! Veeeery useful.

Anonymous said...

could you please guide me if i want to add multichoice functionality with this example what should i do??

i want expandable list with checkboxes too

waiting for you reply.
thanks in advance.

MaDhu Cry said...

I tried to apply checkboxes to expandable list,but it is not worked for me...

Gabor Paller said...

MaDhu Cry, try this post.

Gabor Paller said...

Anonymous, try this line in onCreate:

getExpandableListView().setChoiceMode( ListView.CHOICE_MODE_MULTIPLE );

संदीप said...
This comment has been removed by the author.
संदीप said...

This is only two level of expansion. How can I do a multi-level expansion list. My requirement is of 4 level.

abhijeet said...

Excellent Code !!!! thanx

Gabor Paller said...

"This is only two level of expansion. How can I do a multi-level expansion list. My requirement is of 4 level. "

ExpandableListView can only do 2 levels. If you want more, you have to write your own widget. The source of the platform's ExpandableListView is a good starting point.

Tamalak said...

This is a great guide that helped me a lot!!

BTW, the bug you talked about appears to be fixed. I did the code replacement and it worked fine.

Anonymous said...

Hi all this is good one i want to know one thing.
can we access childname xml file on public boolean onGroupClick(ExpandableListView arg0, View arg1,
int arg2, long arg3)
method???
thank you.

MAD'u CrY said...

Hi,
Actually in my list,I had many childs,and I want to apply separate background for clicked child for differentiating.Is it possible to apply?

Linda said...

The bug with different layouts seem to be solved. Tried it in the emulator with android version 2.2 and it looks good

MAD'u CrY said...

Yah I got it, Thanks Linda..

Harish World said...

Hey
i want to collapse the grey when i click blue or any other group and vice versa how to achieve it any help

MAD'u CrY said...

In ExpandListVIew API, We have setOnGroupCollapseListener and setOnChildCollapseListener. And by using these, we can store previous clicked(opened) group item and use collapseGroup(groupID); we can achieve this. For Clarity please go through the ExpandableListView API..

Thanks

Naman Varshney said...

is it possible to have more than 2 levels of sublist because i need 5 levels of hierarchy. pls help