Contents

In this tutorial, creating a custom ExpandableListView with parent and child rows.

 

Step 1   -
Introduction

ExpandableListView is a view that shows items in a vertically 
scrolling two-level list. It is different to list view as it allows
two levels. 

•	Groups :- Show children items.
•	Items :-  Just like subcategories.

→ Groups that can populate to show particular childs data. Each group
can be expanded or collapsed particularly to show or hide its children
items. Most useful for show information like tree. It also possible to
show multi-level tree. This will we show in coming soon tutorials.In
this tutorial, creating a custom ExpandableListView with parent and
child rows. To really understand and learn something new then start
create an project as a given steps below. In Demo, we implementing
show one group at one time means on click of another group, opened
group will hide. I assume that you have created a new android project.
 

Step 2   -
Output

As an output of following demo, we’ll get list of three types
entertainment. 
These are the parent items. 
The user can then select either of these to display a list of 
child item(s). 
You can see a Toast message when the parent item expands
and when it collapses. 
Selecting one of the child items displays another Toast message
indicating which category was selected and its name.
 

Step 3   -
Create activity and add ExpandableListView : Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ExpandableListView
        android:id="@+id/ExpandableListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="3dp"/>

</LinearLayout>
 

Step 4   -
Create layout for group header and bind in custom adapter. : Row_group.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="wrap_content"
    android:orientation="vertical"
    android:padding="8dp"
    android:background="@color/colorgroup">


    <TextView
        android:id="@+id/groupHeader"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
        android:textSize="20sp" />

</LinearLayout>
 

Step 5   -
Create layout for group child name and bind in custom adapter. : Row_child.xml

<?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="55dip"
    android:orientation="vertical"
    android:background="@color/colorchild">

    <TextView
        android:id="@+id/childItem"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="17dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft" />

</LinearLayout>
 

Step 6   -
Create custom adapter. Here, We will use the BaseExpandableListAdapter to supply and control the data display.

→  In isChildSelectable(int i, int i1) method compulsory return true for child click.
→  if hasStableIds() is true, the returned ID must be stable as well.

ExpandableListAdapter.java

package com.example.expandablelistviewdemo;

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;

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

public class ExpandableListAdapter extends BaseExpandableListAdapter{

    private Context mContext;
    //Group header titles
    private List<String> listGroup; 
    // child data in format of header title, child title
    private HashMap<String, List<String>> listChild;

    public ExpandableListAdapter(Context context, List<String> listGroup,
                                 HashMap<String, List<String>> listChild) {
        this.mContext = context;
        this.listGroup = listGroup;
        this.listChild = listChild;
    }
    
    @Override
    public int getGroupCount() {
        return this.listGroup.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return this.listChild.get(this.listGroup.get(groupPosition)).size();
    }

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

    @Override
    public Object getChild(int groupPosition, int childPosititon) {
        return this.listChild.get(this.listGroup.get(groupPosition)).get(childPosititon);
    }

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

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

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

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {
        String headerTitle = (String) getGroup(groupPosition);
        if (convertView == null) {
            LayoutInflater infalInflater = (LayoutInflater) this.mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.row_group, null);
        }

        TextView lblListHeader =  convertView.findViewById(R.id.groupHeader);
        lblListHeader.setTypeface(null, Typeface.BOLD);
        lblListHeader.setText(headerTitle);

        return convertView;
    }

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

        final String childText = (String) getChild(groupPosition, childPosition);

        if (convertView == null) {
            LayoutInflater infalInflater = (LayoutInflater) this.mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.row_child, null);
        }

        TextView txtListChild =  convertView.findViewById(R.id.childItem);
        txtListChild.setText(childText);
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int i, int i1) {
        return true;
    }
}
 

Step 7   -
Bind data, set group header and child listener.

In class named MainActivity.java, we implement ExpandableListView.
→ OnGroupClickListener()  –   listens for when a group item has been selected.
→ Here we use HashMap  combine group header data and group child  
→ OnGroupExpandListener() – listens for when a group item has been selected. Once selected, it expands and triggers its onGroupExpand() method
→ OnGroupCollapseListener() – listens for when an expanded group item has been selected. Once selected, it collapses and triggers its onGroupCollapse() method
→ OnChildClickListener() – listens for when a child item has been selected. Once selected, it triggers its onChildClick() method

MainActivity.java

package com.example.expandablelistviewdemo;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
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 {

    //initialize
    private Activity activity;
    ExpandableListView expandableListView;
    ExpandableListAdapter expandableListAdapter;
    List<String> groupList;
    HashMap<String, List<String>> childListofGroup;
    private int previousGroup = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = MainActivity.this;
        findViews();
        init();
        setListener();
    }

    private void findViews() {
        expandableListView = findViewById(R.id.ExpandableListView);
    }

    private void init() {
        // load list data
        loadListData();

        //  set adapter
        expandableListAdapter = new ExpandableListAdapter(this, groupList, childListofGroup);
        expandableListView.setAdapter(expandableListAdapter);
    }

    private void setListener() {
        // Listview Group click listener
        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

            @Override
            public boolean onGroupClick(ExpandableListView parent, View v,
                                        int groupPosition, long id) {
                 Toast.makeText(activity,
                         "Group Clicked " + groupList.get(groupPosition),
                         Toast.LENGTH_SHORT).show();

                // only one group is populate using this
                if (expandableListView.isGroupExpanded(groupPosition)) {
                    expandableListView.collapseGroup(groupPosition);
                    previousGroup=-1;
                } else {
                    expandableListView.expandGroup(groupPosition);
                    if(previousGroup!=-1){
                        expandableListView.collapseGroup(previousGroup);
                    }
                    previousGroup=groupPosition;
                }
                return true;
            }
        });

        // Listview Group expanded listener
        expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {

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

        // Listview Group collasped listener
        expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {

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

            }
        });

        // Listview on child click listener
        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

            @Override
            public boolean onChildClick(ExpandableListView parent, View v,
                                        int groupPosition, int childPosition, long id) {
                Toast.makeText(
                        activity,
                        groupList.get(groupPosition)
                                + " : "
                                + childListofGroup.get(
                                groupList.get(groupPosition)).get(
                                childPosition), Toast.LENGTH_SHORT)
                        .show();

                return false;
            }
        });
    }

    private void loadListData() {
        groupList = new ArrayList<String>();
        childListofGroup = new HashMap<String, List<String>>();

        // Adding group header data
        groupList.add("Movie Names");
        groupList.add("Song Names");
        groupList.add("Book Names");

        // Adding child data
        List<String> movies = new ArrayList<String>();
        movies.add("Avengers Infinity War");
        movies.add("Deadpool 2");
        movies.add("Doctor Strange");
        movies.add("Fast and Furious 8");
        movies.add("Ready Player One");

        List<String> music = new ArrayList<String>();
        music.add("Nucleya");
        music.add("Shape of You");
        music.add("Chainsmokers");
        music.add("Back To You");
        music.add("Despacito");

        List<String> books = new ArrayList<String>();
        books.add("Sacred Games");
        books.add("Our Mutual Friend");
        books.add("Story of the Eye");
        books.add("Mahabharata");
        books.add("The Discovery of India\n");

        // Group header, Child data
        childListofGroup.put(groupList.get(0), movies);
        childListofGroup.put(groupList.get(1), music);
        childListofGroup.put(groupList.get(2), books);
    }
}

Got an Idea of Game Development? What are you still waiting for? Contact us now and see the Idea live soon. Our company has been named as one of the best Game Development Company in India.

I am mobile app developer. I am working on android. I love to share designing tips & tricks with you.

Download demo of this blog click on Download button.