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:
MainActivity
: Displays the layout with theExpandableListView
.ExpandableListDataPump
: Represents random data in a list and maps child data items to their respective group headers.CustomExpandableListAdapter
: Provides theMainActivity
with data from theExpandableListDataPump
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.