ANDROID - ExpandableListView
Asked Answered
S

1

10

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.

Schnapp answered 13/4, 2011 at 6:10 Comment(0)
U
17

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).

Unquestioning answered 13/4, 2011 at 7:5 Comment(6)
thanx rekaszeru. Since im a begginer would you care to share a working sample?Schnapp
sure thing, i'll just need a couple of minutes to build up one. It would be usefull though if i'd know from where you get the data (server or local).Unquestioning
if you can, please build a very simple example (just some test data with bunch of arrays). I am getting the data tru sort of XML service designed for the compact framework treeview. I will have to format the data myself, yet at this stage im not sure yet how. Right now im interested in the structure of expendable list itself. Checkboxes, radiobuttons etcSchnapp
thanx alot rekaszeru ! If i may ask one more question... How would you customize children if you wanted the pattern i put in my question. Under one parent i want multiselection on children (checkboxes) in another parent i want a singleselection on children (radio buttons). Nevertheless im acception your anwser because you put your time and knowledge to help a newbie !Schnapp
You can expand the getChildView method of the adapter, and examine whether the parent needs singleselectable or multiselectable children. Also need to create two childrow.xml-s, one for radionbuttons and one for checkboxes. And probably the most clear way for implementing this on data-structure level would be to have an abstract AParent class (with capabilities of the current Parent) and create a SingleSelectParent and a MultiSelectParent extension class based on it. One would have a new member: selectedIndex:int and the MultiSelectParent would have a selectedIndices:ArrayList<int>...Unquestioning
After you determined what kind of AParent you are dealing with, you inflate the appropriate childrow.xml, and set the checked/selected values based on the Child instance's selected memeber.Unquestioning

© 2022 - 2024 — McMap. All rights reserved.