Working with ExpandableListView is very complex, so to make things simpler I will explain you how to use this ListView in few simple steps. This example will display 3 category of vehicles in a ExpandableList.
Firstly we will create the Layout files for Main Activity, GroupView and ChildView Layout.
MainActivity: main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ExpandableListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="fill_parent" >
</ExpandableListView>
</LinearLayout>
GroupView group_layout.xml
ChildView: child_layout.xml
MainActivity: ExpandableListViewAppActivity.java
BaseExpandableListAdapter: ExpandableListAdapter.java
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
public class ExpandableListAdapter extends BaseExpandableListAdapter{
@Override
public boolean areAllItemsEnabled()
{
return true;
}
private Context context;
private ArrayList<String> groups;
private ArrayList<ArrayList<Vehicle>> children;
public ExpandableListAdapter(Context context, ArrayList<String> groups,
ArrayList<ArrayList<Vehicle>> children) {
this.context = context;
this.groups = groups;
this.children = children;
}
/**
* A general add method, that allows you to add a Vehicle to this list
*
* Depending on if the category opf the vehicle is present or not,
* the corresponding item will either be added to an existing group if it
* exists, else the group will be created and then the item will be added
* @param vehicle
*/
public void addItem(Vehicle vehicle) {
if (!groups.contains(vehicle.getGroup())) {
groups.add(vehicle.getGroup());
}
int index = groups.indexOf(vehicle.getGroup());
if (children.size() < index + 1) {
children.add(new ArrayList<Vehicle>());
}
children.get(index).add(vehicle);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return children.get(groupPosition).get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
// Return a child view. You can load your custom layout here.
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
Vehicle vehicle = (Vehicle) getChild(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.child_layout, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tvChild);
tv.setText(" " + vehicle.getName());
// Depending upon the child type, set the imageTextView01
tv.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
/*if (vehicle instanceof Car) {
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.car, 0, 0, 0);
} else if (vehicle instanceof Bus) {
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bus, 0, 0, 0);
} else if (vehicle instanceof Bike) {
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bike, 0, 0, 0);
}*/
return convertView;
}
@Override
public int getChildrenCount(int groupPosition) {
return children.get(groupPosition).size();
}
@Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
@Override
public int getGroupCount() {
return groups.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
// Return a group view. You can load your custom layout here.
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
String group = (String) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.group_layout, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tvGroup);
tv.setText(group);
return convertView;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int arg0, int arg1) {
return true;
}
}
Firstly we will create the Layout files for Main Activity, GroupView and ChildView Layout.
MainActivity: main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ExpandableListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="fill_parent" >
</ExpandableListView>
</LinearLayout>
GroupView group_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView android:id="@+id/ImageView01"
android:layout_height="40dip" android:layout_width="40dip" android:layout_marginLeft="40dip"></ImageView>
<TextView android:id="@+id/tvGroup" android:layout_width="fill_parent"
android:layout_height="45dip" android:text="Groups" android:gravity="center_vertical|right"
android:paddingLeft="5dip" android:paddingRight="5dip"
android:textColor="#ffffffff" android:textStyle="bold"
android:textSize="17dip"></TextView>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView android:id="@+id/ImageView01"
android:layout_height="40dip" android:layout_width="40dip" android:layout_marginLeft="40dip"></ImageView>
<TextView android:layout_width="fill_parent"
android:layout_height="45dip" android:paddingLeft="5dip"
android:paddingRight="5dip" android:textStyle="bold" android:textSize="17dip"
android:gravity="center_vertical" android:id="@+id/tvChild"
android:text="Children" android:textColor="#ffCCCC22"></TextView>
</LinearLayout>
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.Toast;
public class ExpandableListViewAppActivity extends Activity implements Runnable {
private ExpandableListAdapter adapter;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Retrive the ExpandableListView from the layout
ExpandableListView listView = (ExpandableListView) findViewById(R.id.listView);
listView.setOnChildClickListener(new OnChildClickListener()
{
@Override
public boolean onChildClick(ExpandableListView arg0, View arg1, int arg2, int arg3, long arg4)
{
Toast.makeText(getBaseContext(), "Child clicked", Toast.LENGTH_LONG).show();
return false;
}
});
listView.setOnGroupClickListener(new OnGroupClickListener()
{
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
// TODO Auto-generated method stub
Toast.makeText(getBaseContext(), "Group clicked", Toast.LENGTH_LONG).show();
return false;
}
});
// Initialize the adapter with blank groups and children
// We will be adding children on a thread, and then update the ListView
adapter = new ExpandableListAdapter(this, new ArrayList<String>(),
new ArrayList<ArrayList<Vehicle>>());
// Set this blank adapter to the list view
listView.setAdapter(adapter);
Thread thread = new Thread(this);
thread.start();
}
@Override
public void run() {
// TODO Auto-generated method stub
final int ITEMS = 15;
int count = 0;
while (count != ITEMS)
{
count++;
adapter.addItem(MockDataProvider.getRandomVehicle("Vehicle no. " + count));
// Notify the adapter
handler.sendEmptyMessage(1);
try
{
// Sleep for two seconds
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
private Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
adapter.notifyDataSetChanged();
super.handleMessage(msg);
}
};
}
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
public class ExpandableListAdapter extends BaseExpandableListAdapter{
@Override
public boolean areAllItemsEnabled()
{
return true;
}
private Context context;
private ArrayList<String> groups;
private ArrayList<ArrayList<Vehicle>> children;
public ExpandableListAdapter(Context context, ArrayList<String> groups,
ArrayList<ArrayList<Vehicle>> children) {
this.context = context;
this.groups = groups;
this.children = children;
}
/**
* A general add method, that allows you to add a Vehicle to this list
*
* Depending on if the category opf the vehicle is present or not,
* the corresponding item will either be added to an existing group if it
* exists, else the group will be created and then the item will be added
* @param vehicle
*/
public void addItem(Vehicle vehicle) {
if (!groups.contains(vehicle.getGroup())) {
groups.add(vehicle.getGroup());
}
int index = groups.indexOf(vehicle.getGroup());
if (children.size() < index + 1) {
children.add(new ArrayList<Vehicle>());
}
children.get(index).add(vehicle);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return children.get(groupPosition).get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
// Return a child view. You can load your custom layout here.
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
Vehicle vehicle = (Vehicle) getChild(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.child_layout, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tvChild);
tv.setText(" " + vehicle.getName());
// Depending upon the child type, set the imageTextView01
tv.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
/*if (vehicle instanceof Car) {
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.car, 0, 0, 0);
} else if (vehicle instanceof Bus) {
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bus, 0, 0, 0);
} else if (vehicle instanceof Bike) {
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.bike, 0, 0, 0);
}*/
return convertView;
}
@Override
public int getChildrenCount(int groupPosition) {
return children.get(groupPosition).size();
}
@Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
@Override
public int getGroupCount() {
return groups.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
// Return a group view. You can load your custom layout here.
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
String group = (String) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.group_layout, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tvGroup);
tv.setText(group);
return convertView;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int arg0, int arg1) {
return true;
}
}
Vehicle.java
public class Vehicle {
private String name;
private String group;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public Vehicle(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MockDataProvider.java
import java.util.Random;
public class MockDataProvider {
// A utility method that generates random Vehicles
public static Vehicle getRandomVehicle(String name) {
Vehicle vehicle = null;
Random random = new Random();
int type = random.nextInt(3);
switch (type) {
case 0:
vehicle = new Car(name);
break;
case 1:
vehicle = new Bus(name);
break;
case 2:
vehicle = new Bike(name);
break;
}
return vehicle;
}
}
Car.java,Bus.java, Bike.java
public class Bike extends Vehicle{
public Bike(String name) {
super(name);
setGroup("Bikes");
}
}