Tutorial

Android ExpandableListView Example Tutorial

Published on August 3, 2022
author

By Anupam Chugh

Android ExpandableListView Example 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 our products

About the author(s)

Category:
Tutorial
Tags:

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.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
May 4, 2016

how to start an activity if group have no child items and when i clicked on group and if another group having childs if click on group open childs and based on child clik navigate to other views android

- harsha

JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
August 25, 2016

Hi Harsha, I understand that you are looking for a way to start an activity by clicking a group that has no children. Right? There is a setOnGroupClickListener() method that you can invoke and possibly define the intent to the next activity. Thanks.

- Anupam Chugh

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    May 30, 2016

    very neatly put! Thank you

    - elham

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    September 21, 2017

    Thanks elham

    - Anupam Chugh

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      June 2, 2016

      Very good tutorial

      - jaya

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      September 21, 2017

      Thanks Jaya

      - Anupam Chugh

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        June 3, 2016

        Thanks man, great details and greate code!

        - vetdam

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        September 21, 2017

        Thanks vetdam

        - Anupam Chugh

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          July 12, 2016

          Great tutorial! Thank you!

          - Daniel Silva

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          September 21, 2017

          Thanks Daniel

          - Anupam Chugh

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            July 19, 2016

            Great tutorial! I am try these and encountered a problem. exlView = (ExpandableListView) findViewById(R.id.pages_android); exlDetail = ExlDataPump.getData(); exlTitle = new ArrayList(exlDetail.keySet()); exlAdapter = new ExlAdapter(this, exlTitle, exlDetail); exlView.setAdapter(exlAdapter); There brought me an error : invoke setAdapter on null object. What’s wrong???

            - iamlazynic

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            September 25, 2016

            Hi iamlazynic, Seems like exlTitle is empty. Try debugging by printing the fields/nested objects of exlTitle and exlDetail. Thanks

            - Anupam Chugh

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              September 7, 2016

              Thank you Very much, How can do this for populating array from JSON API, i don’t have idea how to populate array values from the JSON. Kindly give some references. Thanks in Advance Mohanraj.

              - Mohanraj

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              September 25, 2016

              Hi Mohanraj, Try iterating over the JSONArray using a for loop. Use ArrayLists to populate data from JSONObjects. Essentially you can keep an ArrayList to store the header values, another to store child data for each header. Use a HashMap to store key value pairs. Ideally, the group header name should be the key and the child ArrayList should be it’s mapped value. Thanks Anupam

              - Anupam Chugh

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                September 18, 2016

                Hello freinds, My quesry is can we add data in expandable list view dynamically through sqlite database, if yes, olease help me with the same. Thanks.

                - sanyog

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                September 21, 2017

                Store the ArrayLists as JSON Strings in the SQLite database. Convert them to ArrayList and vice-versa using Gson Library.

                - Anupam Chugh

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  September 19, 2016

                  Hi i would like to ask, how can i put the arrow down in the right position? Thank you in advance for the help.

                  - Gielany Rosal

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  September 25, 2016

                  Hi Gielany Rosal, Add an ImageView to the right of the group title in the group header view and set the image resource. (Download it from google or use android:src=“@android:drawable/arrow_up_float”, android:src=“@android:drawable/arrow_down_float”). Toggle the image when the group is expanded or collapsed. Hide the left indicator set it as null as shown below. Alternative Solution: Override the method onWindowFocusChanged as shown below. @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { expandableListView.setIndicatorBounds(expandableListView.getWidth()-40,expandableListView.getWidth()); } else { expandableListView.setIndicatorBoundsRelative(expandableListView.getWidth()-40,expandableListView.getWidth()); } } In this case the arrow would look distorted. I’d say go for the first solution. Thanks Anupam

                  - Anupam Chugh

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    September 24, 2016

                    Hi, Nice tutorial How is the parent and child arranged or sorted out?

                    - Ryan

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    September 25, 2016

                    Hi Ryan, The group header and child are sorted out in this case since an ArrayList displays data in chronological order. A HashMap is used to map the respective group header with its child. Thanks Anupam

                    - Anupam Chugh

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

                      Please complete your information!

                      Become a contributor for community

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

                      DigitalOcean Documentation

                      Full documentation for every DigitalOcean product.

                      Resources for startups and SMBs

                      The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                      Get our newsletter

                      Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                      New accounts only. By submitting your email you agree to our Privacy Policy

                      The developer cloud

                      Scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Get started for free

                      Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                      *This promotional offer applies to new accounts only.