Introduction to Android ExpandableListView

This tutorial explains how to implement an ExpandableListView in Android to group list data by categories. This is similar to a menu and submenus in a ListView.

What is an ExpandableListView?

An ExpandableListView displays items in a vertically scrolling two-level list. Unlike a ListView, it allows for easy expansion and collapse of groups by touch. The ExpandableListViewAdapter loads data into the respective elements. Important methods of this class include:

  • setChildIndicator(Drawable): Displays an indicator next to each item representing its current state.
  • setGroupIndicator(Drawable): Draws an indicator next to the group, representing its state (expanded or collapsed).
  • getGroupView(): Returns the view for the list group header.
  • getChildView(): Returns the view for the list child item.

Important Interfaces

The notable interfaces of this class include:

  • ExpandableListView.OnChildClickListener: Overridden to implement the callback method invoked when a child in the expanded list is clicked.
  • ExpandableListView.OnGroupClickListener: Overridden to implement the callback method invoked when a group header in the expanded list is clicked.
  • ExpandableListView.OnGroupCollapseListener: Used to notify when a group is collapsed.
  • ExpandableListView.OnGroupExpandListener: Used to notify when a group is expanded.

Project Structure

This project consists of three classes:

  1. MainActivity: Displays the layout with the ExpandableListView.
  2. ExpandableListDataPump: Represents random data in a list and maps child data items to their respective group headers.
  3. CustomExpandableListAdapter: Provides the MainActivity with data from the ExpandableListDataPump class.

Example Code

The layout file activity_main.xml contains an ExpandableListView in a RelativeLayout.

 <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"
    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>

Implementing the Adapter Class

The CustomExpandableListAdapter class extends BaseExpandableListAdapter and overrides its methods to provide the view for the ExpandableListView.

 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;
    }
}

Main Activity

The MainActivity implements the main interfaces and shows toast messages with the name of the item or the state of the group on each click.

 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;
            }
        });
    }
}

Conclusion

This tutorial shows how to implement an ExpandableListView in Android. This flexible component allows for a clear and organized display of lists with multiple levels and provides an easy way to manage and display grouped data.

Example Project

The complete Android ExpandableListView project can be downloaded from the following link.

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in:

centron Managed Cloud Hosting in Deutschland

Mocking Void Methods with EasyMock

Guide, JavaScript
Mocking Void Methods with EasyMock In unit testing, mocking is an essential technique that allows developers to simulate the behavior of complex objects. EasyMock is a popular framework for creating…