Multi-level ExpandableListView in Android

UnTraDe picture UnTraDe · Jul 21, 2013 · Viewed 17.3k times · Source

I'm trying to create a category tree from a given unknown-size nor level list of categories. So I'm trying to create a general expandable list which can contain 2+ number of levels. The general idea is to add to every child who has childs another ExpandableListView in it's layout. The problem is that the second level wont open, it looks like it rendered it over the child or something. Here's some screen-shots of the result:

Here's how it's look like before openingenter image description here

And after opening the first option: (It's supposed to be empty)

enter image description here

And now open the second one: (Has 4 childs and one of them have childs of his own)

enter image description here

As you can see the third option looks like it's been rendered another option or something on it, after clicking to open it:

enter image description here

It's does nothing except change the state of the arrow. At least it tries to open it...

Here's the code:

CatAdapter.java: (extends BaseExpandableListAdapter) http://pastebin.com/6yUTkMbJ

Category.java: http://pastebin.com/E7yWwpna

catitem.xml: http://pastebin.com/G5MPT3Ua

The usage: http://pastebin.com/Wk0FqJhn

Sorry for the long question, I was trying to be clear as possible.

Thanks in advance! Sorry for my bad english!

Edit:

I ended up making a custom view for this task, thank you all for your answers!

Answer

Geobits picture Geobits · Jul 24, 2013

I believe the problem is in your getChildView() method:

    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

            convertView = inflater.inflate(R.layout.catitem, parent, false);
            TextView textView_catName = (TextView)convertView.findViewById(R.id.textView_catName);
            Category current = categories.get(groupPosition).childs.get(childPosition);
            textView_catName.setText(groupPosition + " , " + childPosition);

            if(current.childs.size() > 0 ) {
                    ExpandableListView elv = new ExpandableListView(context);
                    elv.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,  AbsListView.LayoutParams.WRAP_CONTENT));
                    elv.setAdapter(new CatAdapter(context, current.childs));
                    ((ViewGroup)convertView).addView(elv);
            }

            return convertView;
    }

When you encounter an 'expandable' child, you are still inflating R.layout.catitem and adding your new elv to it. Since the catitem is a RelativeLayout and you don't add any parameters for alignment, each view is placed at the top-left corner, overlaying whatever else is there.

You may want to try changing R.layout.catitem to have a vertical LinearLayout as its root. This should prevent them from overlapping the child's title, but I can't guarantee that the children's children will not still overlap. It's an easy change, though, and worth a shot.


Also, from the docs for ExpandableListView:

Note: You cannot use the value wrap_content for the android:layout_height attribute of a ExpandableListView in XML if the parent's size is also not strictly specified (for example, if the parent were ScrollView you could not specify wrap_content since it also can be any length. However, you can use wrap_content if the ExpandableListView parent has a specific size, such as 100 pixels.

That says "in XML", so I'm not sure if it means to apply to code or not. It seems to me that they'd use the same mechanism, so it might be worth looking into. I'm not sure how you'd go about setting a maximum size for the parent. You may be able to measure the size of one item, and multiply by the number of children. You'd have to take margins/separators into account, so it may not be simple.


If that doesn't work, you may need to roll your own ViewGroup directly. It shouldn't be too hard to implement it from scratch, just don't forget to try to take advantage of view recycling(which your current method doesn't do anyway).