Sunday, December 5, 2010

Expandable list and checkboxes revisited

Once upon a time I wrote a nice little post about checkbox focus problems. The guy who originally asked the question was satisfied and went away. While I was looking the other way, a heated discussion started in the comments because the example program had an annoying property: it did not preserve the state of the check boxes when the groups were opened/collapsed but reordered the check marks randomly. Eventually I got a mail whether I could put my example program right.

Click here to download the example program.

So I did. The cause of the trouble was the adapter backing the expandable list view. It was a SimpleExpandableListAdapter which, as its name implies, is simple. In particular, it is not able to feed data to check boxes because they require boolean state while SimpleExpandableListAdapter supports only Strings (here is a posts that explains the relationship between views and adapters). The solution was to write a custom ExpandableListAdapter and the random check mark reordering disappeared.

66 comments:

Anonymous said...

could you please please upload the correct code using the custom ExpandableAdapter? I keep on getting error trying to do it myself and don't understand what's causing the problem. It would be a great help if you could provide a solution code for the above example.

Gabor Paller said...

Anonymous, please download the example program from the link in the post. It says: "Click here to download the example program."

I have just checked, the example program can be downloaded and executes correctly on the emulator.

Anonymous said...

This code still does not work. Tried the following. Loaded in the emulator. Expanded the first two groups. Selected one child of the second group. Un-expanded, then re-expanded the first group. Notice the second group's children check boxes change.

Appreciate the effort though! This has been the best resource for this stuff out there.

Gabor Paller said...

Anonymous, the example program does exactly it is supposed to do. It demonstrates, how the expandable list can be backed by an expandable list adapter.

What the example program does not do is the update of the adapter's data based on user's interaction. Whatever you do, if you collapse and then reopen a group, you get back the original checkbox state. Implement a logic that updates the adapter's data in the main activity's onChildClick, it is easy to do.

Senthil said...

Hi, Could some one please assist, how to create an expandable list for sub items, Lets take the same example which is explained. In that under the "grey" , each sub-item like "lightgrey", "dimgrey".. should expand and close, and if we close the top item "grey", all the sub-items should be closed in case if it is expanded.

Thanks in Advance.

Gabor Paller said...

Senthil, ExpandableList is hardcoded to handle only 2 levels therefore you cannot implement multi-level expandable list with manipulating the adapter.

Senthil said...

Thanks for your reply, I have a specific requirement which has to be implemented in that way.
Is there any other way to acheive that.?

Any suggestions would be really helpful

Gabor Paller said...

Senthil, get ExpandableListView.java from the Android source tree and enhance it to support your requirement (you have to move it to your own package, out of android.widget).

Using a third-party component would be easier but I am not aware of any.

Senthil said...

Thanks for your suggestion, Keep updating if you come across any such.

Anonymous said...

Thx alot...exactly what i was looking 4.. :)

Senthil said...

Hi Gabor, I have tried multiple ways, but couldn't acheive the multi level expandable list.

Can you please provide any thrid party solution if you have, or can you give some detail insight.

Thanks in Advance.

Gabor Paller said...

What if you added another expandable list as a child row?

zchira said...

It looks like the server is down and I can not download the code :(

bharath said...

Can we place a check box in group row?

Anonymous said...

the code is working but i m unable to set the indicator(grey lefthand side).....what i supposed to do????

Gabor Paller said...

Anonymous, what do you mean by "setting the indicator"? Do you want to have a custom collapse indicator?

herux said...

Hi, i have the same question with @bharath, i've tried that but, the group can not be clicked, except the Button or CheckBox it's self,

you have an idea ?

Anonymous said...

thank you very much, you save my day :D

Amit Gadekar said...

Hi,
Thanks for the article.
Can you please show or guide me how to use more than 1 expandable lists on the activity.
Thanks in advance.

Gabor Paller said...

Amit, you can just put more than one ExpandableListView into the Activity's layout. Also, don't extend ExpandableListActivity, extend simple Activity and register the listeners directly on the ExpandableListView instances.

Anonymous said...

I'm having trouble with the code as well.

For some reason, I'm getting errors in ColorAdapter. On the line that says "color.setText( c.getColor() );" i get the error: "the method getColor is undefined for type Color". I get the same error for the lines "rgb.setText( c.getRgb() );" with getRgb and "cb.setChecked( c.getState() );" with getState().

Likewise, I'm getting an error in the ExpListActivity on all the lines that say "color.add( new Color( "...","...", false ) );"....The error says "The constructor (String, String, bollean) is undefined"

What's up with all this? It seems like Color isn't working right?

Gabor Paller said...

Anonymous, did you insert the code into some other program? Then there is a chance that some other Color from a different package messes up with our Color. You can rename the Color class to something else and see if the problem persists.

Anonymous said...

Never mind. I figured out the problem. I had copied and pasted your code for the activity before I ever created the Color class, so Eclipse was giving me an error and asking me if I want to import the Color class (a different Color class), which I stupidly said "ok" to. Then it was doing the import, and when I created the Color class, my created class wasn't being used.

Thanks for your response!

Jason Hamm said...

I have been working with the expandableList and cannot find a way to delete/add groups on the fly. Could you give me some advice and direction please? Thanks

kenna said...

You can use long click to activate context menu.

kenna said...

Thanks for the code! I'm using it in my program. Everything works OK, but I add several columns to the list. All columns doesn't fit the screen. How can I use horizontal scroll or some of the trick?

Anonymous said...

This doesn't works perfecty becouse when you click on checkbox,activity onChildClick method isn't called.

Use a ImaView instead and set imgaseresource like this

*** ColorAdapter method

public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View v = null;
if( convertView != null )
v = convertView;
else
v = inflater.inflate(R.layout.child_row, parent, false);



ColorBean colorBean = (ColorBean)getChild( groupPosition, childPosition );


ImageView check = (ImageView)v.findViewById( R.id.imageView1 );
if(colorBean.getState()==true)
check.setImageResource(android.R.drawable.checkbox_on_background);
else
check.setImageResource(android.R.drawable.checkbox_off_background);

TextView colorText = (TextView)v.findViewById( R.id.childname );
if( colorText != null )
colorText.setText( colorBean.getColor() );

TextView rgbText = (TextView)v.findViewById( R.id.rgb );
if( rgbText != null )
{
rgbText.setText( colorBean.getRgb() );
rgbText.setBackgroundColor(colorBean.getState()?Color.parseColor(colorBean.getRgb()):Color.TRANSPARENT);
}
return v;
}


*** ExpandableListActivity

public boolean onChildClick(
ExpandableListView parent,
View v,
int groupPosition,
int childPosition,
long id) {

Log.d( LOG_TAG, "onChildClick: "+childPosition );
ColorBean color = (ColorBean) expListAdapter.getChild( groupPosition, childPosition );

color.setState(!color.getState());
expListAdapter.notifyDataSetChanged();
TextView rgbText = (TextView)v.findViewById( R.id.rgb );
rgbText.setBackgroundColor(Color.TRANSPARENT);

return super.onChildClick(parent, v, groupPosition, childPosition, id);
}

jaya said...

Hi ,
I need a sticky header to be created inside Expandablelistview.
Any suggestions on it.

Its basically like a separated list view,when each item is expanded

ponzetti said...

Anonymnous, your solution is perfetc. Thanks!

iamar said...

Muito obrigado

Francesco said...

Hi guys,
how can i save and load the state of checkbox when i pass from an activity to another one?

Thanks

Francesco said...
This comment has been removed by the author.
Aaron said...

Thanks for the tutorial. I was able to learn a lot and change it around for a program that I am writing. I am having trouble figuring out how to keep the state change of the checkbox. I am thinking have a method at the bottom of ElistCBox when the checkbox is clicked that uses getState and then add a method changeState. The problem I am having is when I declare Color c at the method I added in ElistCBox, I need to assign it a position, but cannot figure out how to assign it a position. Am I on the right track and if I am how would I find the position.

Aaron said...

I figured out the problem I was having in my last post. I don't know if it is because I changed around the code or it is an error in the program, but if you click on the test instead of the checkbox, the checkbox changes state and it holds when you expand and shrink the parent. Now I have to figure out how to have that work when the checkbox is clicked

Aaron said...

I figure out how to get the onChildClick to respond when clicked. In the XML you need to enter for the checkbox android:clickable="false"

kinghomer said...

PLS GUYS!!!
HELP ME HERE:

http://stackoverflow.com/questions/9361600/android-custom-expandeble-list-and-checkbox-issue-why-is-chechbox-state-random

i cant implement a custom expandable list. I solved checkbox state problem, but now when i click on a parent node and child is shown, parent nodes swith their position. When all node are expanded, each node is in the right position. ITS INCREDIBLE !!! WHYYY
hheeeelppppp

Gabor Paller said...

kinghomer, you don't seem to set the checkbox state in getGroupView() method. If you don't set any field in a row, view recycling may set it up with random value.

kinghomer said...

Could you revisit page?

andreea said...

When I try to import this project in Eclipse I got a message like this: No projects are found to import.
Can you help me with this problem, please?

Gabor Paller said...

andreea, this project is not an eclipse project but a plain Android SDK project. In order to make an Eclipse project based on it, click New/Android project then choose the "Create project from existing source" option.

andreea said...

Your solution was good, but now I have an other error: AndroidManifest.xml file missing!
:(

andreea said...
This comment has been removed by the author.
Gabor Paller said...

andreea, I imported the project and there was no error. Did you select the top directory of the project in the "Location" input field of the import dialog? You should find an AndroidManifest.xml file in that directory.

andreea said...

Finally I succeed to import the project. Thank you so much for your help and for the project.

Aaron said...

I have been looking through the code. I cannot tell what piece of code executes when you click on the button by grey, blue, etc. What piece of code expands the menu?

Gabor Paller said...

Aaron, that code is inside the ExpandableListView class.

Aaron said...

Gabor, thanks. I needed to find a way to control when the group was clicked on. Looked up the ExpandableListView on the android site and figured it out.

5 little speckled frogs said...

Thanks for posting this code! I am using it successfully. I am new to Android programming and was wondering how to have the checkboxes be checked, based on database content. Currently, I update the arrays when the chekboxes are checked or unchecked and then re-bind the adapter. I need a way to have the values be check if there is data in the database. Any ideas how to check the checkboxes once they have been rendered? Thanks again. Almost there!

bujji said...

Thanks for this solution ,i have implemented based on your solution i got everything.now i have to implement mutipleselction thing .and how can i searchbox can be implement in expandible can you please elaborate how to do this.

Divya said...

Hi Gabor,
Thanks a lot...

Sanket Patel said...

hello,
you have given very good example but can you tell me how can i maintain checkbox selection ! because when i uncheck/check and then select another parent item, my selection reset at starting bind items. :(
i change your 'cb.toggle();' to below but not working !!

if( cb != null ){
if(cb.isChecked()){
cb.setChecked(false);
}else
{
cb.setChecked(true);
}

}

Please solve it.!

Gabriel Pozo said...

This post is very instructive, thanks!!!

Seyfeddine Aloui said...

thank you for this great post :)

i have a problem here : i understand now how you can save the original states of the checkboxes but how can i maintain checkbox selection ! what should i do in onChildClick() method? i think that i must rebind the adapter ...but i can't find a way to do it!

please give me what should i do

Gabor Paller said...

Hi, Seyfeddine,

The example program takes the description of the list elements from a two-level list structure.

ArrayList> colors = new ArrayList>();

Second-level lists store Color instances that have a field variable called "state". That field is used to initialize the checkbox states.

Now this example program does not feed back checkbox state changes into this structure. So what you should do in onChildClick is to update the "state" field of the appropriate instance. When the Activity dies, save the state list in onSaveInstanceState. Restore this state into that list in onCreate if that method is called with a non-null Bundle parameter (that Bundle will give you back the state you saved in onSaveInstanceState).

Seyfeddine Aloui said...

Hi Gabor,

thank you for your answer, but i'm not talking about the states after the activity die but about saving the states after unexpanding the parentrow ... i added a method called setState in Color.java class and i added this code in onChildClick() method:

public boolean onChildClick(
ExpandableListView parent,
View v,
int groupPosition,
int childPosition,
long id) {
Log.d( LOG_TAG, "onChildClick: "+childPosition );
CheckBox cb = (CheckBox)v.findViewById( R.id.check1 );
Color c = (Color)getChild( groupPosition, childPosition );
if( cb != null ){
if (c.state==true){
cb.setChecked(false);
c.setState(false);
}
else
{
cb.setChecked(true);
c.setState(true);
}

}
but nothing is changing ! help :/

Seyfeddine Aloui said...

Hey Man!

i found a solution you must make difference between clicking on checkbox and clicking on the childrow ! so let's make it editable just from the onChildClick() method just by editing the child_row.xml file :


thank you man ;)

mutkan said...

Hi,

I customized your code and add to checkbox feature, but there is a problem and i'm going to be crazy to solve it.

I want to check all checkbox from the parent root group but it does check one.

Could you help me on this issue?

Thank you so much.

The Customize Android project link : https://mega.co.nz/#!tw8gjJ5Y!XDYFvTYBObRJUVa2dLAGxTMtXBcwYpOvY4nfejt7U40

Anonymous said...

Bhai Na avadtu hoy to blog na lakhaso pan ramado nahi please yaar time waste thay chhe !!!!

Jayashree Behera said...

When i am clicking on child class it is setting the checkboxes. can you help me to work on checkboxes. I want if i will click on checkbox it will show the group position as well as child position.

Gabor Paller said...

Jayashree Behera, please check the comments, particularly the ones from Seyfeddine Aloui.

Jayashree Behera said...

Gabor Paller ... thanx for considering my comments. Actally i want to click on checkboxes not in child position.

Gabor Paller said...

Jayashree Behera, so where are those check boxes? In group level position or outside the expandable list?

Jayashree Behera said...

Gober Paller thanx for responding my comment... my check box is in child class.

Jayashree Behera said...

Gober Paller Please help me it's very urgent i want to click on checkbox of child class.

Jayashree Behera said...

Gober Paller Please help me it's very urgent i want to click on checkbox of child class.

Jayashree Behera said...

Gober Paller Please help me it's very urgent i want to click on checkbox of child class.