ANDROID - ExpandableListView

no9 picture no9 · Apr 13, 2011 · Viewed 12.1k times · Source

Im trying to figure out how to build a view that contains (many of):

  • PARENT1 (checkable, expandable)
  • CHILD1 (RADIO BUTTON)
  • CHILD2 (RADIO BUTTON)

...

  • PARENT2 (checkable, expandable)
  • CHILD1 (CHECKABLE)
  • CHILD2 (CHECKABLE)

...

The point is that parent has to be checkable and based on that children have to change the icon. Can some1 point me into the right direction, because from what i found nothing seems to work for me.

Answer

rekaszeru picture rekaszeru · Apr 13, 2011

I assume, you have your data structured somehow, like: ArrayList where

  • Parent {name:String, checked:boolean, children:ArrayList} and
  • Child {name:String}

If so, you only have to do two things:

  1. create proper layouts for your parent item renderer and child item renderer
  2. expand BaseExpandableListAdapter to build up the list yourself.

Here is a small sample about what's in my mind:
layout/grouprow.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:orientation="horizontal"
    android:gravity="fill" android:layout_width="fill_parent"
    android:layout_height="wrap_content" 
    xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- PARENT -->
    <TextView android:id="@+id/parentname" android:paddingLeft="5px"
        android:paddingRight="5px" android:paddingTop="3px"
        android:paddingBottom="3px" android:textStyle="bold" android:textSize="18px"
        android:layout_gravity="fill_horizontal" android:gravity="left"
        android:layout_height="wrap_content" android:layout_width="wrap_content" />
    <CheckBox android:id="@+id/checkbox" android:focusable="false" 
        android:layout_alignParentRight="true" android:freezesText="false"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:layout_marginTop="5px" />
</RelativeLayout>

layout/childrow.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="wrap_content" android:padding="0px">
    <!-- CHILD -->
    <TextView android:id="@+id/childname" android:paddingLeft="15px" 
        android:paddingRight="5px" android:focusable="false" android:textSize="14px"
        android:layout_marginLeft="10px" android:layout_marginRight="3px" 
        android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>

MyELAdapter class: I've declared this as an inner class of MyExpandableList, so i could reach the list of parents directly, without having to pass it as parameter.

private class MyELAdapter extends BaseExpandableListAdapter
{
    private LayoutInflater inflater;

    public MyELAdapter()
    {
        inflater = LayoutInflater.from(MyExpandableList.this);
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, 
            View convertView, ViewGroup parentView)
    {
        final Parent parent = parents.get(groupPosition);
        convertView = inflater.inflate(R.layout.grouprow, parentView, false);
        ((TextView) convertView.findViewById(R.id.parentname)).setText(parent.getName());
        CheckBox checkbox = (CheckBox) convertView.findViewById(R.id.checkbox);
        checkbox.setChecked(parent.isChecked());
        checkbox.setOnCheckedChangeListener(new CheckUpdateListener(parent));
        if (parent.isChecked())
            convertView.setBackgroundResource(R.color.red);
        else
            convertView.setBackgroundResource(R.color.blue);
        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, 
            View convertView, ViewGroup parentView)
    {
        final Parent parent = parents.get(groupPosition);
        final Child child = parent.getChildren().get(childPosition);
        convertView = inflater.inflate(R.layout.childrow, parentView, false);
        ((TextView) convertView.findViewById(R.id.childname)).setText(child.getName());
        return convertView;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition)
    {
        return parents.get(groupPosition).getChildren().get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition)
    {
        return childPosition;
    }

    @Override
    public int getChildrenCount(int groupPosition)
    {
        return parents.get(groupPosition).getChildren().size();
    }

    @Override
    public Object getGroup(int groupPosition)
    {
        return parents.get(groupPosition);
    }

    @Override
    public int getGroupCount()
    {
        return parents.size();
    }

    @Override
    public long getGroupId(int groupPosition)
    {
        return groupPosition;
    }

    @Override
    public void notifyDataSetChanged()
    {
        super.notifyDataSetChanged();
    }

    @Override
    public boolean isEmpty()
    {
        return ((parents == null) || parents.isEmpty());
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition)
    {
        return true;
    }

    @Override
    public boolean hasStableIds()
    {
        return true;
    }

    @Override
    public boolean areAllItemsEnabled()
    {
        return true;
    }
}

You must already have a public class MyExpandableList extends ExpandableListActivity which has a member:

private ArrayList<Parent> parents;

after you assign a value to this member / load the list of parents, you should also attach your adapter to this view:

this.setListAdapter(new MyELAdapter());

and that's it. You have a checkable-expandable list, and in the CheckUpdateListener's onCheckedChanged(CompoundButton buttonView, boolean isChecked) method you can update your parent object's checked state.

Note, that the background color is determined in the getGroupView method, so you don't have to change it anywhere, just call the adapter's notifyDataSetChanged() method if needed.

Update

you can download the sample source code from this link. It is an eclipse project, but if you are using other developer environment, just simply copy the necessary source files (java + xml).