Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Android ExpandablelistView Tutorial with Android Custom Adaptor

4.62/5 (5 votes)
1 Nov 2016CPOL4 min read 53.4K  
In this Android ExpandablelistView tutorial, we will learn how to make an expandable list in Android.

In this Android ExpandablelistView tutorial, we will learn how to make an expandable list in Android. You can think of this as an expandable list android example. Here, we will make a custom ExpandablelistView where a user can add data in the menu and can even add a new category. For adding new category, we will use a dropdown Spinner and a search button will be used to add products in that category.

Android ExpandablelistView TutorialAndroid ExpandablelistView Tutorial Android ExpandablelistView Tutorial

What is Android ExpandableListView

Android expandable list view shows items in a vertically scrolling two-level list. This differs from the ListView by allowing two levels: groups which can individually be expanded to show its children. You can attach listeners to the Android ExpandableListView to listen for OnClick events on the Group or the individual children.

In this tutorial, we will make different sections of categories like Vegetables, Fruits, Grocery, Books, etc. In each category, we can have different products. For example: We can add Asparagus, Potato, Cabbage in Vegetables, etc. We will break this Android Expandable list view in parts so that you will be able to easily understand the code along with its application.

Creating a New Project

  1. Go to File → New → New Project and enter your Application Name (We have named as Android ExpandablelistView Tutorial).
  2. Enter Company Domain, this is used to uniquely identify your App’s package worldwide.
  3. Choose project location and minimum SDK and on the next screen, choose Empty Activity, since we would be adding most of the code ourselves. Then Click on Next.
  4. Choose an Activity Name. Make sure Generate Layout File check box is selected, otherwise we have to generate it ourselves.Then click on Finish. We have left Activity Name as MainActivity.
  5. Gradle will configure your project and resolve the dependencies. Once it is complete, proceed to the next steps. Make sure build.grade has the following code especially compile ‘com.android.support:appcompat-v7:23.1.1’:

First, we will discuss its Layout and then we will go through MainActivity.

Layout of Android ExpandablelistView Tutorial

In the layout, we will have a spinner to select the category, a button to add a product and finally an Android expandable list view.

activity_main.xml

XML
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
                xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent" 
                android:layout_height="match_parent"
                android:orientation="vertical">

    <Spinner android:id="@+id/department" 
    android:layout_width="match_parent"
             android:layout_height="wrap_content" 
             android:layout_alignParentLeft="true"
             android:layout_alignParentTop="true" 
             android:textStyle="bold" 
             android:paddingLeft="100sp"/>

    <Button android:id="@+id/add" 
    android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:layout_alignParentRight="true"
            android:layout_below="@id/department" 
            android:text="Add" />

    <EditText android:id="@+id/product" 
    android:layout_width="fill_parent"
              android:layout_height="wrap_content" 
              android:layout_alignBaseline="@id/add"
              android:layout_alignParentLeft="true" 
              android:layout_below="@id/department"
              android:layout_toLeftOf="@id/add" 
              android:ems="10" 
              android:hint="Enter Product for above Category"
              android:inputType="text" />

    <TextView android:id="@+id/textView1" 
    android:layout_width="match_parent"
              android:layout_height="wrap_content" 
              android:layout_alignParentLeft="true"
              android:layout_below="@id/product" 
              android:layout_margin="5dp"
              android:background="#000080" 
              android:padding="5dp"
              android:text="Different Categories with Products..." 
              android:textAppearance="?android:attr/textAppearanceMedium"
              android:textStyle="bold" android:textColor="#ffffff"/>

    <ExpandableListView android:id="@+id/myList"
                        android:layout_width="match_parent" 
                        android:layout_height="fill_parent"
                        android:layout_below="@id/textView1" />

</RelativeLayout>

Also, create two more layout files namely child_row.xml and group_heading.xml in the same path as activity_main.xml. child_row.xml will be responsible for generating layout for products added under different categories of Android expandable list view and group_heading.xml will generate view for main category.

child_row.xml

XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="match_parent" 
android:layout_height="match_parent"
                android:orientation="vertical" 
                xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/sequence"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:paddingLeft="35sp"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/childItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@id/sequence"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

group_heading.xml

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="55dip"
              android:orientation="vertical" >

    <TextView
        android:id="@+id/heading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="35sp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textStyle="bold" />

</LinearLayout>

Make following changes in strings.xml:

XML
<resources>
    <string name="app_name">"Android Expandablelistview Tutorial "
    </string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_main">MainActivity</string>

    <string-array name="dept_array">
        <item>Vegetable</item>
        <item>Fruits</item>
        <item>Grocery</item>
        <item>Electronics</item>
        <item>Books</item>
        <item>Language</item>
    </string-array>

</resources>

So, layout of Android ExpandablelistView Tutorial is complete. Now, let’s move to adding life to the layout, i.e., MainActivity.

Android Custom Adaptor

Create a new Java file named MyListAdapter.java at the same path where MainActivity.java is located, i.e., …/AndroidExpandablelistviewTutorial/app/src/main/java/com/androidtutorialpoint/androidexpandablelistviewtutorial/MyListAdapter.java.

An adapter links an Android ExpandableListView with the underlying data. The implementation of this interface will provide access to the data of the children (categorized by groups), and also instantiate Views for children and groups.

MyListAdapter.java

Java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;

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 MyListAdapter extends BaseExpandableListAdapter {

    private Context context;
    private ArrayList<HeaderInfo> deptList;

    public MyListAdapter(Context context, ArrayList<HeaderInfo> deptList) {
        this.context = context;
        this.deptList = deptList;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        ArrayList<DetailInfo> productList = 
        deptList.get(groupPosition).getProductList();
        return productList.get(childPosition);
    }

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

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
                             View view, ViewGroup parent) {

        DetailInfo detailInfo = (DetailInfo) getChild(groupPosition, childPosition);
        if (view == null) {
            LayoutInflater infalInflater = (LayoutInflater) 
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = infalInflater.inflate(R.layout.child_row, null);
        }

        TextView sequence = (TextView) view.findViewById(R.id.sequence);
        sequence.setText(detailInfo.getSequence().trim() + ") ");
        TextView childItem = (TextView) view.findViewById(R.id.childItem);
        childItem.setText(detailInfo.getName().trim());

        return view;
    }

    @Override
    public int getChildrenCount(int groupPosition) {

        ArrayList<DetailInfo> productList = 
        deptList.get(groupPosition).getProductList();
        return productList.size();
    }

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

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

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

    @Override
    public View getGroupView(int groupPosition, boolean isLastChild, View view,
                             ViewGroup parent) {

        HeaderInfo headerInfo = (HeaderInfo) getGroup(groupPosition);
        if (view == null) {
            LayoutInflater inf = (LayoutInflater) 
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inf.inflate(R.layout.group_heading, null);
        }

        TextView heading = (TextView) view.findViewById(R.id.heading);
        heading.setText(headerInfo.getName().trim());

        return view;
    }

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

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

Also, we will make separate classes for type of category and products associated with them. Category will be of type HeaderInfo. Create a class named HeaderInfo.java at the same path as MyListAdapter.java.

HeaderInfo.java

Java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;

import java.util.ArrayList;

public class HeaderInfo {

    private String name;
    private ArrayList<DetailInfo> productList = new ArrayList<DetailInfo>();

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public ArrayList<DetailInfo> getProductList() {
        return productList;
    }
    public void setProductList(ArrayList<DetailInfo> productList) {
        this.productList = productList;
    }
}

Products in each category will be of type DetailInfo so create a new class named DetailInfo.java:

DetailInfo.java

Java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;

public class DetailInfo {

    private String sequence = "";
    private String name = "";

    public String getSequence() {
        return sequence;
    }
    public void setSequence(String sequence) {
        this.sequence = sequence;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Finally, we will fill categories and products in our list of Android ExpandablelistView Tutorial through MainActivity.

MainActivity.java

Java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;

import java.util.ArrayList;
import java.util.LinkedHashMap;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;

public class MainActivity extends AppCompatActivity implements OnClickListener {

    private LinkedHashMap<String, HeaderInfo> mySection = new LinkedHashMap<>();
    private ArrayList<HeaderInfo> SectionList = new ArrayList<>();

    private MyListAdapter listAdapter;
    private ExpandableListView expandableListView;

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

        Spinner spinner = (Spinner) findViewById(R.id.department);
        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
                R.array.dept_array, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);

        //Just add some data to start with
        AddProduct();

        //get reference to the ExpandableListView
        expandableListView = (ExpandableListView) findViewById(R.id.myList);
        //create the adapter by passing your ArrayList data
        listAdapter = new MyListAdapter(MainActivity.this, SectionList);
        //attach the adapter to the list
        expandableListView.setAdapter(listAdapter);

        //expand all Groups
        expandAll();

        //add new item to the List
        Button add = (Button) findViewById(R.id.add);
        add.setOnClickListener(this);

        //listener for child row click
        expandableListView.setOnChildClickListener(myListItemClicked);
        //listener for group heading click
        expandableListView.setOnGroupClickListener(myListGroupClicked);
    }

    public void onClick(View v) {

        switch (v.getId()) {

            //add entry to the List
            case R.id.add:

                Spinner spinner = (Spinner) findViewById(R.id.department);
                String department = spinner.getSelectedItem().toString();
                EditText editText = (EditText) findViewById(R.id.product);
                String product = editText.getText().toString();
                editText.setText("");

                //add a new item to the list
                int groupPosition = addProduct(department,product);
                //notify the list so that changes can take effect
                listAdapter.notifyDataSetChanged();

                //collapse all groups
                collapseAll();
                //expand the group where item was just added
                expandableListView.expandGroup(groupPosition);
                //set the current group to be selected so that it becomes visible
                expandableListView.setSelectedGroup(groupPosition);

                break;
        }
    }

    //method to expand all groups
    private void expandAll() {
        int count = listAdapter.getGroupCount();
        for (int i = 0; i < count; i++){
            expandableListView.expandGroup(i);
        }
    }

    //method to collapse all groups
    private void collapseAll() {
        int count = listAdapter.getGroupCount();
        for (int i = 0; i < count; i++){
            expandableListView.collapseGroup(i);
        }
    }

    //load some initial data into out list
    private void AddProduct(){

        addProduct("Vegetable","Potato");
        addProduct("Vegetable","Cabbage");
        addProduct("Vegetable","Onion");

        addProduct("Fruits","Apple");
        addProduct("Fruits","Orange");
    }

    //our child listener
    private OnChildClickListener myListItemClicked =  new OnChildClickListener() {

        public boolean onChildClick(ExpandableListView parent, View v,
                                    int groupPosition, int childPosition, long id) {

            //get the group header
            HeaderInfo headerInfo = SectionList.get(groupPosition);
            //get the child info
            DetailInfo detailInfo =  headerInfo.getProductList().get(childPosition);
            //display it or do something with it
            Toast.makeText(getBaseContext(), "Clicked on Detail " + headerInfo.getName()
                    + "/" + detailInfo.getName(), Toast.LENGTH_LONG).show();
            return false;
        }
    };

    //our group listener
    private OnGroupClickListener myListGroupClicked =  new OnGroupClickListener() {

        public boolean onGroupClick(ExpandableListView parent, View v,
                                    int groupPosition, long id) {

            //get the group header
            HeaderInfo headerInfo = SectionList.get(groupPosition);
            //display it or do something with it
            Toast.makeText(getBaseContext(), "Child on Header " + headerInfo.getName(),
                    Toast.LENGTH_LONG).show();

            return false;
        }
    };

    //here we maintain our products in various departments
    private int addProduct(String department, String product){

        int groupPosition = 0;

        //check the hash map if the group already exists
        HeaderInfo headerInfo = mySection.get(department);
        //add the group if doesn't exists
        if(headerInfo == null){
            headerInfo = new HeaderInfo();
            headerInfo.setName(department);
            mySection.put(department, headerInfo);
            SectionList.add(headerInfo);
        }

        //get the children for the group
        ArrayList<DetailInfo> productList = headerInfo.getProductList();
        //size of the children list
        int listSize = productList.size();
        //add to the counter
        listSize++;

        //create a new child and add that to the group
        DetailInfo detailInfo = new DetailInfo();
        detailInfo.setSequence(String.valueOf(listSize));
        detailInfo.setName(product);
        productList.add(detailInfo);
        headerInfo.setProductList(productList);

        //find the group position inside the list
        groupPosition = SectionList.indexOf(headerInfo);
        return groupPosition;
    }
}

In the above code, we are first creating a spinner to add category in our list and associating it with an ArrayAdapter. This ArrayAdapter is filled with items as defined in Strings.xml as string-array. After filling Spinner, we are adding products one by one in Expandable list view through addProduct(). In the addProduct(), we are first checking if product is already present. If it's not, then it is added to the corresponding category. Also, this function is returning position of the product in that category which is utilized in onClick(). After adding initial products, we attached MyListAdapter with the expandable list view. This adapter will be responsible for adding and updating data in the list view. Finally, listeners are added for button such that whenever button is clicked, onClick() will be called and new item is added into the list.

Let us tell you how this expandable list Android example is working overall. First of all, some items will be added automatically through AddProduct(). Now if a user wants to add a new category and product in ExpandableListView, then Button Add will come into the picture. On clicking Add button, products will be added through groupPosition = addProduct(department,product) and ExpandableListView will be updated through listAdapter.notifyDataSetChanged() so that changes can take effect.

So finally our Android ExpandablelistView Tutorial is complete. Run this App and add new categories and products in ExpandablelistView as you want. You can also refer to the demo of Android ExpandablelistView Tutorial given at the start of the tutorial.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)