In this example creating a custom Expandable ListView with parent and child rows. parent rows contains texts,images and a checkbox. child rows contains texts,images. Creating custom adapter to create Expandable ListView rows .
1. Create Model classes for parent rows(Parent.java) and for child rows(Child.java). These classes will use to store parent rows and child rows data.
2. Create xml files for parent rows(grouprow.xml) and for child rows(childrow.xml). These classes will use to create GUI for parent rows and child rows.
3. Create dummy data in parent and child model objects and Store objects in an ArrayList.
4. Create custom Adapter MyExpandableListAdapter class and inflate grouprow.xml file for parent rows() and childrow.xml for child rows. Use Models Parent.java and Child.java to create data for rows.

- It will store data for parent rows.
import java.util.ArrayList;
public class Parent
{
private String name;
private String text1;
private String text2;
private String checkedtype;
private boolean checked;
// ArrayList to store child objects
private ArrayList children;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getText1()
{
return text1;
}
public void setText1(String text1)
{
this.text1 = text1;
}
public String getText2()
{
return text2;
}
public void setText2(String text2)
{
this.text2 = text2;
}
public String getCheckedType()
{
return checkedtype;
}
public void setCheckedType(String checkedtype)
{
this.checkedtype = checkedtype;
}
public boolean isChecked()
{
return checked;
}
public void setChecked(boolean checked)
{
this.checked = checked;
}
// ArrayList to store child objects
public ArrayList getChildren()
{
return children;
}
public void setChildren(ArrayList children)
{
this.children = children;
}
}
- It will store data for child rows.
public class Child
{
private String name;
private String text1;
private String text2;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getText1()
{
return text1;
}
public void setText1(String text1)
{
this.text1 = text1;
}
public String getText2()
{
return text2;
}
public void setText2(String text2)
{
this.text2 = text2;
}
}
- It will inflate in adapter to create parent rows gui.
<?xml version="1.0" encoding="utf-8"?>
<!-- PARENT -->
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/bar_bg"
android:stretchColumns="1" android:paddingTop="0dip" android:layout_gravity="top"
>
<TableRow>
<ImageView
android:id="@+id/image"
android:layout_width="50dip"
android:layout_height="50dip"
android:layout_marginLeft="10dip"/>
<TableLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1" android:paddingTop="0dip" android:layout_gravity="top"
>
<TableRow>
<TextView
android:id="@+id/text1"
android:textColor="#000000"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1" android:layout_gravity="left|center_vertical"
android:textSize="17dip" android:layout_marginLeft="10dip"
android:textStyle="bold"/>
</TableRow>
<TableRow >
<TextView
android:id="@+id/text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="#000000"
android:layout_weight="1" android:layout_gravity="left|center_vertical"
android:textSize="14dip" android:layout_marginLeft="10dip"/>
</TableRow>
</TableLayout>
<ImageView
android:layout_width="25dip"
android:id="@+id/rightcheck"
android:layout_height="25dip"
android:layout_gravity="left|center_vertical"
android:scaleType="centerCrop"
android:background="@drawable/rightcheck"
/>
<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" />
</TableRow>
</TableLayout>
- It will inflate in adapter to create child rows gui.
<?xml version="1.0" encoding="utf-8"?>
<!-- CHILD -->
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1" android:paddingTop="0dip" android:layout_gravity="top"
> <!-- android:background="@drawable/bar_bg" -->
<TableRow>
<ImageView
android:id="@+id/image"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_marginLeft="30dip"/>
<TableLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="1" android:paddingTop="0dip" android:layout_gravity="top"
>
<TableRow>
<TextView
android:id="@+id/text1"
style="@style/SettingCodeFontBold"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1" android:layout_gravity="left|center_vertical"
android:textSize="12dip" android:layout_marginLeft="30dip"
/>
</TableRow>
<TableRow >
<TextView
android:id="@+id/text"
style="@style/SettingCodeFont"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1" android:layout_gravity="left|center_vertical"
android:textSize="12dip" android:layout_marginLeft="30dip"/>
</TableRow>
</TableLayout>
</TableRow>
</TableLayout>
- Please see comments i have put comments for each lines code.
import com.androidexample.customexpandablelist.R;
import java.util.ArrayList;
import android.os.Bundle;
import android.app.ExpandableListActivity;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;
public class ExpandableListMain extends ExpandableListActivity
{
//Initialize variables
private static final String STR_CHECKED = " has Checked!";
private static final String STR_UNCHECKED = " has unChecked!";
private int ParentClickStatus=-1;
private int ChildClickStatus=-1;
private ArrayList<Parent> parents;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Resources res = this.getResources();
Drawable devider = res.getDrawable(R.drawable.line);
// Set ExpandableListView values
getExpandableListView().setGroupIndicator(null);
getExpandableListView().setDivider(devider);
getExpandableListView().setChildDivider(devider);
getExpandableListView().setDividerHeight(1);
registerForContextMenu(getExpandableListView());
//Creating static data in arraylist
final ArrayList<Parent> dummyList = buildDummyData();
// Adding ArrayList data to ExpandableListView values
loadHosts(dummyList);
}
/**
* here should come your data service implementation
* @return
*/
private ArrayList<Parent> buildDummyData()
{
// Creating ArrayList of type parent class to store parent class objects
final ArrayList<Parent> list = new ArrayList<Parent>();
for (int i = 1; i < 4; i++)
{
//Create parent class object
final Parent parent = new Parent();
// Set values in parent class object
if(i==1){
parent.setName("" + i);
parent.setText1("Parent 0");
parent.setText2("Disable App On \nBattery Low");
parent.setChildren(new ArrayList<Child>());
// Create Child class object
final Child child = new Child();
child.setName("" + i);
child.setText1("Child 0");
//Add Child class object to parent class object
parent.getChildren().add(child);
}
else if(i==2){
parent.setName("" + i);
parent.setText1("Parent 1");
parent.setText2("Auto disable/enable App \n at specified time");
parent.setChildren(new ArrayList<Child>());
final Child child = new Child();
child.setName("" + i);
child.setText1("Child 0");
parent.getChildren().add(child);
final Child child1 = new Child();
child1.setName("" + i);
child1.setText1("Child 1");
parent.getChildren().add(child1);
}
else if(i==3){
parent.setName("" + i);
parent.setText1("Parent 1");
parent.setText2("Show App Icon on \nnotification bar");
parent.setChildren(new ArrayList<Child>());
final Child child = new Child();
child.setName("" + i);
child.setText1("Child 0");
parent.getChildren().add(child);
final Child child1 = new Child();
child1.setName("" + i);
child1.setText1("Child 1");
parent.getChildren().add(child1);
final Child child2 = new Child();
child2.setName("" + i);
child2.setText1("Child 2");
parent.getChildren().add(child2);
final Child child3 = new Child();
child3.setName("" + i);
child3.setText1("Child 3");
parent.getChildren().add(child3);
}
//Adding Parent class object to ArrayList
list.add(parent);
}
return list;
}
private void loadHosts(final ArrayList<Parent> newParents)
{
if (newParents == null)
return;
parents = newParents;
// Check for ExpandableListAdapter object
if (this.getExpandableListAdapter() == null)
{
//Create ExpandableListAdapter Object
final MyExpandableListAdapter mAdapter = new MyExpandableListAdapter();
// Set Adapter to ExpandableList Adapter
this.setListAdapter(mAdapter);
}
else
{
// Refresh ExpandableListView data
((MyExpandableListAdapter)getExpandableListAdapter()).notifyDataSetChanged();
}
}
/**
* A Custom adapter to create Parent view (Used grouprow.xml) and Child View((Used childrow.xml).
*/
private class MyExpandableListAdapter extends BaseExpandableListAdapter
{
private LayoutInflater inflater;
public MyExpandableListAdapter()
{
// Create Layout Inflator
inflater = LayoutInflater.from(ExpandableListMain.this);
}
// This Function used to inflate parent rows view
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parentView)
{
final Parent parent = parents.get(groupPosition);
// Inflate grouprow.xml file for parent rows
convertView = inflater.inflate(R.layout.grouprow, parentView, false);
// Get grouprow.xml file elements and set values
((TextView) convertView.findViewById(R.id.text1)).setText(parent.getText1());
((TextView) convertView.findViewById(R.id.text)).setText(parent.getText2());
ImageView image=(ImageView)convertView.findViewById(R.id.image);
image.setImageResource(
getResources().getIdentifier(
"com.androidexample.customexpandablelist:drawable/setting"+parent.getName(),null,null));
ImageView rightcheck=(ImageView)convertView.findViewById(R.id.rightcheck);
//Log.i("onCheckedChanged", "isChecked: "+parent.isChecked());
// Change right check image on parent at runtime
if(parent.isChecked()==true){
rightcheck.setImageResource(
getResources().getIdentifier(
"com.androidexample.customexpandablelist:drawable/rightcheck",null,null));
}
else{
rightcheck.setImageResource(
getResources().getIdentifier(
"com.androidexample.customexpandablelist:drawable/button_check",null,null));
}
// Get grouprow.xml file checkbox elements
CheckBox checkbox = (CheckBox) convertView.findViewById(R.id.checkbox);
checkbox.setChecked(parent.isChecked());
// Set CheckUpdateListener for CheckBox (see below CheckUpdateListener class)
checkbox.setOnCheckedChangeListener(new CheckUpdateListener(parent));
return convertView;
}
// This Function used to inflate child rows view
@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);
// Inflate childrow.xml file for child rows
convertView = inflater.inflate(R.layout.childrow, parentView, false);
// Get childrow.xml file elements and set values
((TextView) convertView.findViewById(R.id.text1)).setText(child.getText1());
ImageView image=(ImageView)convertView.findViewById(R.id.image);
image.setImageResource(
getResources().getIdentifier(
"com.androidexample.customexpandablelist:drawable/setting"+parent.getName(),null,null));
return convertView;
}
@Override
public Object getChild(int groupPosition, int childPosition)
{
//Log.i("Childs", groupPosition+"= getChild =="+childPosition);
return parents.get(groupPosition).getChildren().get(childPosition);
}
//Call when child row clicked
@Override
public long getChildId(int groupPosition, int childPosition)
{
/****** When Child row clicked then this function call *******/
//Log.i("Noise", "parent == "+groupPosition+"= child : =="+childPosition);
if( ChildClickStatus!=childPosition)
{
ChildClickStatus = childPosition;
Toast.makeText(getApplicationContext(), "Parent :"+groupPosition + " Child :"+childPosition ,
Toast.LENGTH_LONG).show();
}
return childPosition;
}
@Override
public int getChildrenCount(int groupPosition)
{
int size=0;
if(parents.get(groupPosition).getChildren()!=null)
size = parents.get(groupPosition).getChildren().size();
return size;
}
@Override
public Object getGroup(int groupPosition)
{
Log.i("Parent", groupPosition+"= getGroup ");
return parents.get(groupPosition);
}
@Override
public int getGroupCount()
{
return parents.size();
}
//Call when parent row clicked
@Override
public long getGroupId(int groupPosition)
{
Log.i("Parent", groupPosition+"= getGroupId "+ParentClickStatus);
if(groupPosition==2 && ParentClickStatus!=groupPosition){
//Alert to user
Toast.makeText(getApplicationContext(), "Parent :"+groupPosition ,
Toast.LENGTH_LONG).show();
}
ParentClickStatus=groupPosition;
if(ParentClickStatus==0)
ParentClickStatus=-1;
return groupPosition;
}
@Override
public void notifyDataSetChanged()
{
// Refresh List rows
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;
}
/******************* Checkbox Checked Change Listener ********************/
private final class CheckUpdateListener implements OnCheckedChangeListener
{
private final Parent parent;
private CheckUpdateListener(Parent parent)
{
this.parent = parent;
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
Log.i("onCheckedChanged", "isChecked: "+isChecked);
parent.setChecked(isChecked);
((MyExpandableListAdapter)getExpandableListAdapter()).notifyDataSetChanged();
final Boolean checked = parent.isChecked();
Toast.makeText(getApplicationContext(),
"Parent : "+parent.getName() + " " + (checked ? STR_CHECKED : STR_UNCHECKED),
Toast.LENGTH_LONG).show();
}
}
/***********************************************************************/
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidexample.customexpandablelist"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.androidexample.customexpandablelist.ExpandableListMain"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>