Expandable ListView Example

Project Description

  • In this example, we will see how to create a simple expandable list view using Android’s ExpandableListView widget. Also we will see how to delete child items in listview
  • Expandable List allows two levels – groups which can individually be expanded to show its children.
  • This example uses custom adapter for ExpandableListView which extends BaseExpandableListAdapter to populate items associated with this view.
  • This expandable list view example explains the following;
    • Each child item has a TextView and ImageView with delete icon.
    • Toast is displayed when a child item is clicked. This is processed in setOnChildClickListener.
    • Child item can be deleted by clicking on delete icon.

Output

Environment Used

Android Project

Create an Android project and name it as ExpandableListDemo.

strings.xml

Open res/values/strings.xml and replace it with following content.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ExpandableListDemo</string>
    <string name="hello_world">Hello world!</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_expandable_list">ExpandableList Demo</string>
</resources>

XML layout files

This Android Expandable List View requires three layout files; one for displaying the main layout containing the ExpandableListView, one for group item layout and other for child item layout.

activity_main.xml

This XML layout file (activity_main.xml) is used for defining ExpandableListView.

Open activity_main.xml file in res/layout and copy the following content.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ExpandableListView
        android:id="@+id/laptop_list"
        android:layout_width="match_parent"
        android:layout_height="fill_parent" >
    </ExpandableListView>
    
</RelativeLayout>

group_item.xml

Create a new layout file group_item.xml in res/layout and copy the following content.

<?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"
    android:padding="10dp" >

    <TextView 
        android:id="@+id/laptop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="25dp"/>
</LinearLayout>

This group layout contains a TextView to display laptop brand.

child_item.xml

Create a new layout file child_item.xml in res/layout and copy the following content.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="10dp" >

    <TextView
        android:id="@+id/laptop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:paddingLeft="25dp" />

    <ImageView
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:src="@drawable/ic_delete" 
        android:contentDescription="@string/app_name"/>

</RelativeLayout>

Each child item contains one TextView to display laptop brand’s model and one ImageView with delete icon to delete a child item.

Create Android Custom Adapter class for ExpandableListView

Create a new Java class “ExpandableListAdapter.java” in package “com.theopentutorials.expandablelist.adapters” and copy the following code.

package com.theopentutorials.expandablelist.adapters;

import java.util.List;
import java.util.Map;

import com.theopentutorials.expandablelist.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ExpandableListAdapter extends BaseExpandableListAdapter {

	private Activity context;
	private Map<String, List<String>> laptopCollections;
	private List<String> laptops;

	public ExpandableListAdapter(Activity context, List<String> laptops,
			Map<String, List<String>> laptopCollections) {
		this.context = context;
		this.laptopCollections = laptopCollections;
		this.laptops = laptops;
	}

	public Object getChild(int groupPosition, int childPosition) {
		return laptopCollections.get(laptops.get(groupPosition)).get(childPosition);
	}

	public long getChildId(int groupPosition, int childPosition) {
		return childPosition;
	}
	
	
	public View getChildView(final int groupPosition, final int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		final String laptop = (String) getChild(groupPosition, childPosition);
		LayoutInflater inflater = context.getLayoutInflater();
		
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.child_item, null);
		}
		
		TextView item = (TextView) convertView.findViewById(R.id.laptop);
		
		ImageView delete = (ImageView) convertView.findViewById(R.id.delete);
		delete.setOnClickListener(new OnClickListener() {
			
			public void onClick(View v) {
				AlertDialog.Builder builder = new AlertDialog.Builder(context);
				builder.setMessage("Do you want to remove?");
				builder.setCancelable(false);
				builder.setPositiveButton("Yes",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								List<String> child = 
									laptopCollections.get(laptops.get(groupPosition));
								child.remove(childPosition);
								notifyDataSetChanged();
							}
						});
				builder.setNegativeButton("No",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								dialog.cancel();
							}
						});
				AlertDialog alertDialog = builder.create();
				alertDialog.show();
			}
		});
		
		item.setText(laptop);
		return convertView;
	}

	public int getChildrenCount(int groupPosition) {
		return laptopCollections.get(laptops.get(groupPosition)).size();
	}

	public Object getGroup(int groupPosition) {
		return laptops.get(groupPosition);
	}

	public int getGroupCount() {
		return laptops.size();
	}

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

	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
		String laptopName = (String) getGroup(groupPosition);
		if (convertView == null) {
			LayoutInflater infalInflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = infalInflater.inflate(R.layout.group_item,
					null);
		}
		TextView item = (TextView) convertView.findViewById(R.id.laptop);
		item.setTypeface(null, Typeface.BOLD);
		item.setText(laptopName);
		return convertView;
	}

	public boolean hasStableIds() {
		return true;
	}

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

This class uses BaseExpandableListAdapter for creating custom adapter for ExpandableListView.

Activity

Open your activity class and copy the following code.

package com.theopentutorials.expandablelist;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.theopentutorials.expandablelist.adapters.ExpandableListAdapter;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.Toast;

public class MainActivity extends Activity {

	List<String> groupList;
	List<String> childList;
	Map<String, List<String>> laptopCollection;
	ExpandableListView expListView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		createGroupList();

		createCollection();

		expListView = (ExpandableListView) findViewById(R.id.laptop_list);
		final ExpandableListAdapter expListAdapter = new ExpandableListAdapter(
				this, groupList, laptopCollection);
		expListView.setAdapter(expListAdapter);

		//setGroupIndicatorToRight();

		expListView.setOnChildClickListener(new OnChildClickListener() {

			public boolean onChildClick(ExpandableListView parent, View v,
					int groupPosition, int childPosition, long id) {
				final String selected = (String) expListAdapter.getChild(
						groupPosition, childPosition);
				Toast.makeText(getBaseContext(), selected, Toast.LENGTH_LONG)
						.show();

				return true;
			}
		});
	}

	private void createGroupList() {
		groupList = new ArrayList<String>();
		groupList.add("HP");
		groupList.add("Dell");
		groupList.add("Lenovo");
		groupList.add("Sony");
		groupList.add("HCL");
		groupList.add("Samsung");
	}

	private void createCollection() {
		// preparing laptops collection(child)
		String[] hpModels = { "HP Pavilion G6-2014TX", "ProBook HP 4540",
				"HP Envy 4-1025TX" };
		String[] hclModels = { "HCL S2101", "HCL L2102", "HCL V2002" };
		String[] lenovoModels = { "IdeaPad Z Series", "Essential G Series",
				"ThinkPad X Series", "Ideapad Z Series" };
		String[] sonyModels = { "VAIO E Series", "VAIO Z Series",
				"VAIO S Series", "VAIO YB Series" };
		String[] dellModels = { "Inspiron", "Vostro", "XPS" };
		String[] samsungModels = { "NP Series", "Series 5", "SF Series" };

		laptopCollection = new LinkedHashMap<String, List<String>>();

		for (String laptop : groupList) {
			if (laptop.equals("HP")) {
				loadChild(hpModels);
			} else if (laptop.equals("Dell"))
				loadChild(dellModels);
			else if (laptop.equals("Sony"))
				loadChild(sonyModels);
			else if (laptop.equals("HCL"))
				loadChild(hclModels);
			else if (laptop.equals("Samsung"))
				loadChild(samsungModels);
			else
				loadChild(lenovoModels);

			laptopCollection.put(laptop, childList);
		}
	}

	private void loadChild(String[] laptopModels) {
		childList = new ArrayList<String>();
		for (String model : laptopModels)
			childList.add(model);
	}

	private void setGroupIndicatorToRight() {
		/* Get the screen width */
		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		int width = dm.widthPixels;

		expListView.setIndicatorBounds(width - getDipsFromPixel(35), width
				- getDipsFromPixel(5));
	}

	// Convert pixel to dip
	public int getDipsFromPixel(float pixels) {
		// Get the screen's density scale
		final float scale = getResources().getDisplayMetrics().density;
		// Convert the dps to pixels, based on density scale
		return (int) (pixels * scale + 0.5f);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}
}

  • Here, we create list of group items stored in ‘groupList’. We create list of child items for each group stored in ‘childList’.
  • We then create a Map where each key is a group name (in this example, it is a laptop brand name) and each value is a list of child (in this example, it is list of laptop models in particular brand).
  • We then create expandable list view adapter and set this adapter in ExpandableListView.
  • In setOnChildClickListener, we display a Toast.
  • By default the group indicator is displayed in left hand side. If you want to display it in right hand side then uncomment “//setGroupIndicatorToRight();” line inside onCreate().
    • We get the width of the screen and use setIndicatorBounds(int left, int right) method to set the indicator bounds for the group view of an expandable list view.

AndroidManifest.xml

Define the activity in AndroidManifest.xml file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.theopentutorials.expandablelist"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.theopentutorials.expandablelist.MainActivity"
            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>

Output

Run your application and you will get the output as shown in the beginning of this tutorial.

If you want to set the group indicator of expandable list to right side then uncomment the highlighted line in onCreate() method. You will get the output as shown below.

Project Folder Structure

The complete folder structure of this example is shown below.

Leave a Comment