Mastering Custom ExpandableListView in Android
- Published on
Mastering Custom ExpandableListView in Android
The ExpandableListView in Android is a powerful UI component that allows you to create a list where items can be expanded or collapsed. It is particularly useful when you need to display hierarchical data. In this blog post, we will explore how to implement a custom ExpandableListView in Android, utilizing best practices and optimized code snippets.
What is ExpandableListView?
An ExpandableListView groups elements into parent-child relationships. When a parent item is clicked, its child items expand, revealing nested information. This is ideal for displaying categories and subcategories, much like a file directory structure.
Setting Up Your Project
Before diving into coding, make sure you have a basic Android project set up. You can create a new project using Android Studio with an empty activity.
Dependencies
No additional dependencies are necessary for the standard ExpandableListView because it is part of the Android framework. However, if you plan on using libraries for better visualization or data handling (like Retrofit for networking), make sure to include them in your build.gradle
file.
Creating the Layout
Let's start with the layout for our ExpandableListView. In your res/layout
folder, create an XML file named activity_main.xml
and add the following code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ExpandableListView
android:id="@+id/expandableListView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
This layout contains only the ExpandableListView
which will fill the entire screen.
Defining Data Structure
Before we start coding, we need to define the data we want to display. Create a HashMap
with the data structure that contains parent and child items.
import android.os.Bundle;
import android.widget.ExpandableListView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ExpandableListView expandableListView;
private ExpandableListAdapter expandableListAdapter;
private List<String> listDataHeader;
private HashMap<String, List<String>> listDataChild;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
expandableListView = findViewById(R.id.expandableListView);
prepareListData();
expandableListAdapter = new ExpandableListAdapter(this, listDataHeader, listDataChild);
expandableListView.setAdapter(expandableListAdapter);
}
private void prepareListData() {
listDataHeader = new ArrayList<>();
listDataChild = new HashMap<>();
listDataHeader.add("Fruit");
listDataHeader.add("Vegetables");
listDataHeader.add("Dairy");
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
List<String> vegetables = new ArrayList<>();
vegetables.add("Carrot");
vegetables.add("Broccoli");
vegetables.add("Peas");
List<String> dairy = new ArrayList<>();
dairy.add("Milk");
dairy.add("Cheese");
dairy.add("Yogurt");
listDataChild.put(listDataHeader.get(0), fruits);
listDataChild.put(listDataHeader.get(1), vegetables);
listDataChild.put(listDataHeader.get(2), dairy);
}
}
Explanation:
- HashMap and List: This code creates a mapping from parent headers to their corresponding child items.
- Prepare Data: The
prepareListData()
method initializes the headers and children for the ExpandableListView.
Creating the Adapter
For the ExpandableListView to work, it requires an adapter. Create a new Java class named ExpandableListAdapter
that extends BaseExpandableListAdapter
.
import android.content.Context;
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 context;
private List<String> expandableListTitle;
private HashMap<String, List<String>> expandableListDetail;
public ExpandableListAdapter(Context context, List<String> expandableListTitle,
HashMap<String, List<String>> expandableListDetail) {
this.context = context;
this.expandableListTitle = expandableListTitle;
this.expandableListDetail = expandableListDetail;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return this.expandableListDetail.get(this.expandableListTitle.get(groupPosition)).get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@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 inflater = (LayoutInflater) this.context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_1, null);
}
TextView txtListChild = convertView.findViewById(android.R.id.text1);
txtListChild.setText(childText);
return convertView;
}
@Override
public int getChildrenCount(int groupPosition) {
return this.expandableListDetail.get(this.expandableListTitle.get(groupPosition)).size();
}
@Override
public Object getGroup(int groupPosition) {
return this.expandableListTitle.get(groupPosition);
}
@Override
public int getGroupCount() {
return this.expandableListTitle.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
String headerTitle = (String) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) this.context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_activated_1, null);
}
TextView lblListHeader = convertView.findViewById(android.R.id.text1);
lblListHeader.setText(headerTitle);
return convertView;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
Explanation:
- View Holder Pattern: Instead of directly finding views each time, we are inflating the layout once and reusing it, enhancing performance.
- Methods: The adapter overrides necessary methods like
getChild
,getGroup
, andgetChildrenCount
.
Running Your App
Now that you have set up your ExpandableListView and adapter, run your app. You should see a list with expandable children when the headers are clicked.
Customizing Your ExpandableListView
The above example uses Android's built-in layouts
for simplicity. You can create custom layouts for your parent and child items to enhance your app's UI. For example, to customize the layout for parent items:
- Create a new layout XML file,
list_group.xml
:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/lblListHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:textSize="18sp"
android:textStyle="bold"
android:background="#D6E9C6"/>
- Update
getGroupView
in theExpandableListAdapter
to use this layout.
The Bottom Line
The ExpandableListView is a highly useful UI component in Android that can display hierarchical data efficiently. By creating a custom adapter and managing your data with HashMap and Lists, you can tailor the component to fit your app's needs.
For more information on Android components, check out the official Android documentation. Experiment with colors, dimensions, and styles to make your expandable list fit nicely into your app's theme.
With this implementation, you can easily expand your app's functionality. The ExpandableListView can easily be integrated with other UI components, making it versatile and flexible. Happy coding!