Tutorial

Android ExpandableListView Example Tutorial

Published on August 3, 2022
Default avatar

By Anupam Chugh

Android ExpandableListView Example Tutorial

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Welcome to Android ExpandableListView Example Tutorial. In this tutorial we’ll implement an ExpandableListView which is used to group list data by categories. It’s sort of menu and submenus in a Android ListView.

Android ExpandableListView

Android ExpandableListView is a view that shows items in a vertically scrolling two-level list. It differs from a ListView by allowing two levels which are groups that can be easily expanded and collapsed by touching to view and their respective children items. ExpandableListViewAdapter in android loads the data into the items associated with this view. Following are some important methods that are used by this class :

  • setChildIndicator(Drawable) : This is used to show an indicator besides each item representing the current state. If the child is the last child for a group, the state state_last will be set
  • setGroupIndicator(Drawable) : An indicator is drawn besides the group representing its state i.e. expanded or collapsed. If the group is empty, the state state_empty will be set. If the group is expanded, the state state_expanded will be set
  • getGroupView() : It returns view for the list group header
  • getChildView() : It returns view for list child item

The notable interfaces that are implemented by this class are given below :

  • ExpandableListView.OnChildClickListener : This is overridden to implement the callback method that’s invoked when a child in the expanded list is clicked
  • ExpandableListView.OnGroupClickListener : This is overridden to implement the callback method that’s invoked when a group header in the expanded list is clicked
  • ExpandableListView.OnGroupCollapseListener : It is used for notifying when a group is collapsed
  • ExpandableListView.OnGroupExpandListener : It is used to notify when a group is expanded

Android ExpandableListView Project Structure

Android ExpandableListView, Android ExpandableListView example, ExpandableListView in android This project consists of three classes.

  • A MainActivity that shows the layout with the ExpandableListView
  • An ExpandableListDataPump which represents a random data in a List and maps the child item data to the respective group headers using a HashMap
  • A CustomExpandableListAdapter which provides the MainActivity with the data from the ExpandableListDataPump class/li>

Android ExpandableListView Code

The activity_main.xml layout consists of an ExpandableListView in a RelativeLayout as shown below : activity_main.xml

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <ExpandableListView
        android:id="@+id/expandableListView"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:indicatorLeft="?android:attr/expandableListPreferredItemIndicatorLeft"
        android:divider="@android:color/darker_gray"
        android:dividerHeight="0.5dp" />

</RelativeLayout>

The android:indicatorLeft is the left bound for an items indicator. Note : We cannot use the value wrap_content for the android:layout_height attribute of the ExpandableListView in XML unless the parent’s size is strictly specified The layout of the group header of each individual list is given below : list_group.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/listTitle"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
        android:textColor="@android:color/black"
        android:paddingTop="10dp"
        android:paddingBottom="10dp" />
</LinearLayout>

The layout row of the child items is given below : list_item.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/expandedListItem"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
        android:paddingTop="10dp"
        android:paddingBottom="10dp" />
</LinearLayout>

The ExpandableListDataPump class is defined as below:

package com.journaldev.expandablelistview;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ExpandableListDataPump {
    public static HashMap<String, List<String>> getData() {
        HashMap<String, List<String>> expandableListDetail = new HashMap<String, List<String>>();

        List<String> cricket = new ArrayList<String>();
        cricket.add("India");
        cricket.add("Pakistan");
        cricket.add("Australia");
        cricket.add("England");
        cricket.add("South Africa");

        List<String> football = new ArrayList<String>();
        football.add("Brazil");
        football.add("Spain");
        football.add("Germany");
        football.add("Netherlands");
        football.add("Italy");

        List<String> basketball = new ArrayList<String>();
        basketball.add("United States");
        basketball.add("Spain");
        basketball.add("Argentina");
        basketball.add("France");
        basketball.add("Russia");

        expandableListDetail.put("CRICKET TEAMS", cricket);
        expandableListDetail.put("FOOTBALL TEAMS", football);
        expandableListDetail.put("BASKETBALL TEAMS", basketball);
        return expandableListDetail;
    }
}

In the above code the expandableListDetail object is used to map the group header strings to their respective children using an ArrayList of Strings. CustomExpandableListAdapter.java

package com.journaldev.expandablelistview;

import java.util.HashMap;
import java.util.List;
import android.content.Context;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

public class CustomExpandableListAdapter extends BaseExpandableListAdapter {

    private Context context;
    private List<String> expandableListTitle;
    private HashMap<String, List<String>> expandableListDetail;

    public CustomExpandableListAdapter(Context context, List<String> expandableListTitle,
                                       HashMap<String, List<String>> expandableListDetail) {
        this.context = context;
        this.expandableListTitle = expandableListTitle;
        this.expandableListDetail = expandableListDetail;
    }

    @Override
    public Object getChild(int listPosition, int expandedListPosition) {
        return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
                .get(expandedListPosition);
    }

    @Override
    public long getChildId(int listPosition, int expandedListPosition) {
        return expandedListPosition;
    }

    @Override
    public View getChildView(int listPosition, final int expandedListPosition,
                             boolean isLastChild, View convertView, ViewGroup parent) {
        final String expandedListText = (String) getChild(listPosition, expandedListPosition);
        if (convertView == null) {
            LayoutInflater layoutInflater = (LayoutInflater) this.context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = layoutInflater.inflate(R.layout.list_item, null);
        }
        TextView expandedListTextView = (TextView) convertView
                .findViewById(R.id.expandedListItem);
        expandedListTextView.setText(expandedListText);
        return convertView;
    }

    @Override
    public int getChildrenCount(int listPosition) {
        return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
                .size();
    }

    @Override
    public Object getGroup(int listPosition) {
        return this.expandableListTitle.get(listPosition);
    }

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

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

    @Override
    public View getGroupView(int listPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {
        String listTitle = (String) getGroup(listPosition);
        if (convertView == null) {
            LayoutInflater layoutInflater = (LayoutInflater) this.context.
                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = layoutInflater.inflate(R.layout.list_group, null);
        }
        TextView listTitleTextView = (TextView) convertView
                .findViewById(R.id.listTitle);
        listTitleTextView.setTypeface(null, Typeface.BOLD);
        listTitleTextView.setText(listTitle);
        return convertView;
    }

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

    @Override
    public boolean isChildSelectable(int listPosition, int expandedListPosition) {
        return true;
    }
}

This class extends BaseExpandableListAdapter and it overrides the methods in the base class to provide the view for the ExpandableListView. getView() fills in the data into the item’s view with the given index. MainActivity.java

package com.journaldev.expandablelistview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    ExpandableListView expandableListView;
    ExpandableListAdapter expandableListAdapter;
    List<String> expandableListTitle;
    HashMap<String, List<String>> expandableListDetail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        expandableListView = (ExpandableListView) findViewById(R.id.expandableListView);
        expandableListDetail = ExpandableListDataPump.getData();
        expandableListTitle = new ArrayList<String>(expandableListDetail.keySet());
        expandableListAdapter = new CustomExpandableListAdapter(this, expandableListTitle, expandableListDetail);
        expandableListView.setAdapter(expandableListAdapter);
        expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {

            @Override
            public void onGroupExpand(int groupPosition) {
                Toast.makeText(getApplicationContext(),
                        expandableListTitle.get(groupPosition) + " List Expanded.",
                        Toast.LENGTH_SHORT).show();
            }
        });

        expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {

            @Override
            public void onGroupCollapse(int groupPosition) {
                Toast.makeText(getApplicationContext(),
                        expandableListTitle.get(groupPosition) + " List Collapsed.",
                        Toast.LENGTH_SHORT).show();

            }
        });

        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v,
                                        int groupPosition, int childPosition, long id) {
                Toast.makeText(
                        getApplicationContext(),
                        expandableListTitle.get(groupPosition)
                                + " -> "
                                + expandableListDetail.get(
                                expandableListTitle.get(groupPosition)).get(
                                childPosition), Toast.LENGTH_SHORT
                ).show();
                return false;
            }
        });
    }

}

In the above code we’ve implemented all the interfaces that were discussed before. For the sake of simplicity, we’ll only display a Toast with the name of the item or the state of the group for every click. But these can be easily modified to perform any other operations. Below is our app with android expandable list view in action. ExpandableListView, android ExpandableListView, android ExpandableListView example Note: ExpandableListViews are scrollable by default. This brings an end to Android ExpandableListView tutorial. You can download the final Android ExpandableListView Project from the below link.

Download Android ExpandableListView Example Project

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Anupam Chugh

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
August 3, 2021

how can i add + and - button for adding new child item and removing exiting child item

- mg

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    March 5, 2021

    can you please help me with a bottom loading inside expandable list

    - sharookh

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      January 5, 2021

      how can i get child view on click of expandable group

      - Deepak Kumar

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        November 17, 2020

        Can this be adapted to show in a Simple Dialog Builder model?

        - George Russell

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          February 12, 2020

          sir Thanks for this tutorial. i have one doubt, how can i create dynamic expandable listview. the groups and childrens data from json data(webservices). in my code two arrays getting “category and items” dynamicaly view category and corresponding items ,how can done?

          - sari

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            December 12, 2019

            Hi, is it possible to shift the position of items? Like according to your example I want to change the team position as first to display basketball then wants to display cricket after I want to display football Team. if we can do this then how could we do this? can you please explain?

            - Jyoti

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              December 8, 2019

              This was very helpful and easy to understand. Thank you.

              - Alif Hasnain

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                October 9, 2019

                This is really great job. THANKS

                - Junaidu Tanko

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  September 14, 2019

                  Hi , how can i populate Expendable listView using Json from Volley and mysql (I have 2 tables Article and comments ), otherly how can i use dynamic data thank you

                  - Ram M

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    August 20, 2019

                    Hello Team, Thank you for the detailed information. A very nice example for a beginner.

                    - Maruti Nedre

                      Try DigitalOcean for free

                      Click below to sign up and get $200 of credit to try our products over 60 days!

                      Sign up

                      Join the Tech Talk
                      Success! Thank you! Please check your email for further details.

                      Please complete your information!

                      Get our biweekly newsletter

                      Sign up for Infrastructure as a Newsletter.

                      Hollie's Hub for Good

                      Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

                      Become a contributor

                      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                      Welcome to the developer cloud

                      DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Learn more
                      DigitalOcean Cloud Control Panel