Android ExpandableListView Search Filter Example

In this example we create an expandable listview which is basically a list where the entries can be expanded to view the child entries which is kind of a sub-list within the main list and with the help of the SearchView onQueryTextChange listener we implement a filter on List Adapter to only display data that the user is searching for. You can customize the filter based on your need to implement more complex logic. If you want to learn more about the ExpandableListView then please visit the following tutorial...
Android ExpandableListView Example.

Android ExpandableListView Search Filter Example
Android ExpandableListView Filter

Android Manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.as400samplecode"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Application main Layout - activity_main.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

 <SearchView android:id="@+id/search" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_alignParentTop="true" android:iconifiedByDefault="false" />



 <ExpandableListView android:id="@+id/expandableList"
  android:layout_width="match_parent" android:layout_height="wrap_content"
  android:layout_alignParentLeft="true" android:layout_below="@+id/search" />


</RelativeLayout>

ExpandableListView Child Row Layout - child_row.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="match_parent"
 android:layout_height="match_parent" android:orientation="vertical"
 xmlns:android="http://schemas.android.com/apk/res/android">

 <TextView android:id="@+id/code" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentLeft="true"
  android:layout_alignParentTop="true" android:paddingLeft="35sp"
  android:paddingRight="10dp" android:text="USA"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/name" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentTop="true"
  android:layout_toRightOf="@+id/code" android:text="United States Of America"
  android:textAppearance="?android:attr/textAppearanceMedium" />

 <TextView android:id="@+id/population" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:layout_alignParentRight="true"
  android:layout_alignParentTop="true" android:paddingRight="5dp"
  android:text="200,000,000" android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

ExpandableListView Header Row Layout - group_row.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="55dip"
 android:orientation="vertical">

 <TextView android:id="@+id/heading" android:layout_width="wrap_content"
  android:layout_height="wrap_content" android:paddingLeft="35sp"
  android:textAppearance="?android:attr/textAppearanceLarge"
  android:textStyle="bold" />

</LinearLayout>

Child Row Country Object - Country.java

package com.as400samplecode;

public class Country {
 
 private String code = "";
 private String name = "";
 private int population = 0;
 
 public Country(String code, String name, int population) {
  super();
  this.code = code;
  this.name = name;
  this.population = population;
 }
 
 public String getCode() {
  return code;
 }
 public void setCode(String code) {
  this.code = code;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public int getPopulation() {
  return population;
 }
 public void setPopulation(int population) {
  this.population = population;
 }
 
}

Group Heading Continent Object - Continent.java

package com.as400samplecode;

import java.util.ArrayList;

public class Continent {
 
 private String name;
 private ArrayList<Country> countryList = new ArrayList<Country>();
 
 public Continent(String name, ArrayList<Country> countryList) {
  super();
  this.name = name;
  this.countryList = countryList;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public ArrayList<Country> getCountryList() {
  return countryList;
 }
 public void setCountryList(ArrayList<Country> countryList) {
  this.countryList = countryList;
 };
 

}

Custom BaseExpandableListAdapter with filter - MyListAdapter.java

package com.as400samplecode;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Locale;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

public class MyListAdapter extends BaseExpandableListAdapter {

 private Context context;
 private ArrayList<Continent> continentList;
 private ArrayList<Continent> originalList;
 
 public MyListAdapter(Context context, ArrayList<Continent> continentList) {
  this.context = context;
  this.continentList = new ArrayList<Continent>();
  this.continentList.addAll(continentList);
  this.originalList = new ArrayList<Continent>();
  this.originalList.addAll(continentList);
 }
 
 @Override
 public Object getChild(int groupPosition, int childPosition) {
  ArrayList<Country> countryList = continentList.get(groupPosition).getCountryList();
  return countryList.get(childPosition);
 }

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

 @Override
 public View getChildView(int groupPosition, int childPosition, boolean isLastChild, 
   View view, ViewGroup parent) {
  
  Country country = (Country) getChild(groupPosition, childPosition);
  if (view == null) {
   LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   view = layoutInflater.inflate(R.layout.child_row, null);
  }
  
  TextView code = (TextView) view.findViewById(R.id.code);
  TextView name = (TextView) view.findViewById(R.id.name);
  TextView population = (TextView) view.findViewById(R.id.population);
  code.setText(country.getCode().trim());
  name.setText(country.getName().trim());
  population.setText(NumberFormat.getNumberInstance(Locale.US).format(country.getPopulation()));
  
  return view;
 }

 @Override
 public int getChildrenCount(int groupPosition) {
  
  ArrayList<Country> countryList = continentList.get(groupPosition).getCountryList();
  return countryList.size();

 }

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

 @Override
 public int getGroupCount() {
  return continentList.size();
 }

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

 @Override
 public View getGroupView(int groupPosition, boolean isLastChild, View view,
   ViewGroup parent) {
  
  Continent continent = (Continent) getGroup(groupPosition);
  if (view == null) {
   LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   view = layoutInflater.inflate(R.layout.group_row, null);
  }
  
  TextView heading = (TextView) view.findViewById(R.id.heading);
  heading.setText(continent.getName().trim());
  
  return view;
 }

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

 @Override
 public boolean isChildSelectable(int groupPosition, int childPosition) {
  return true;
 }
 
 public void filterData(String query){
  
  query = query.toLowerCase();
  Log.v("MyListAdapter", String.valueOf(continentList.size()));
  continentList.clear();
  
  if(query.isEmpty()){
   continentList.addAll(originalList);
  }
  else {
   
   for(Continent continent: originalList){
    
    ArrayList<Country> countryList = continent.getCountryList();
    ArrayList<Country> newList = new ArrayList<Country>();
    for(Country country: countryList){
     if(country.getCode().toLowerCase().contains(query) ||
       country.getName().toLowerCase().contains(query)){
      newList.add(country);
     }
    }
    if(newList.size() > 0){
     Continent nContinent = new Continent(continent.getName(),newList);
     continentList.add(nContinent);
    }
   }
  }
  
  Log.v("MyListAdapter", String.valueOf(continentList.size()));
  notifyDataSetChanged();
  
 }

}

Application Activity - MainActivity.java

package com.as400samplecode;

import java.util.ArrayList;

import android.os.Bundle;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Context;
import android.view.Menu;
import android.widget.ExpandableListView;
import android.widget.SearchView;

public class MainActivity extends Activity implements  
 SearchView.OnQueryTextListener, SearchView.OnCloseListener{

 private SearchView search;
 private MyListAdapter listAdapter;
 private ExpandableListView myList;
 private ArrayList<Continent> continentList = new ArrayList<Continent>();
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
  search = (SearchView) findViewById(R.id.search);
  search.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
  search.setIconifiedByDefault(false);
  search.setOnQueryTextListener(this);
  search.setOnCloseListener(this);
  
  //display the list
  displayList();
  //expand all Groups
  expandAll();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
    //method to expand all groups
 private void expandAll() {
  int count = listAdapter.getGroupCount();
  for (int i = 0; i < count; i++){
   myList.expandGroup(i);
  }
 }
 
 //method to expand all groups
 private void displayList() {
  
  //display the list
  loadSomeData();
  
  //get reference to the ExpandableListView
  myList = (ExpandableListView) findViewById(R.id.expandableList);
  //create the adapter by passing your ArrayList data
  listAdapter = new MyListAdapter(MainActivity.this, continentList);
  //attach the adapter to the list
  myList.setAdapter(listAdapter);
  
 }
 
 private void loadSomeData() {
  
  ArrayList<Country> countryList = new ArrayList<Country>();
  Country country = new Country("BMU","Bermuda",10000000);
  countryList.add(country);
  country = new Country("CAN","Canada",20000000);
  countryList.add(country);
  country = new Country("USA","United States",50000000);
  countryList.add(country);
  
  Continent continent = new Continent("North America",countryList);
  continentList.add(continent);
  
  countryList = new ArrayList<Country>();
  country = new Country("CHN","China",10000100);
  countryList.add(country);
  country = new Country("JPN","Japan",20000200);
  countryList.add(country);
  country = new Country("THA","Thailand",50000500);
  countryList.add(country);
  
  continent = new Continent("Asia",countryList);
  continentList.add(continent);
  
 }

 @Override
 public boolean onClose() {
  listAdapter.filterData("");
  expandAll();
  return false;
 }

 @Override
 public boolean onQueryTextChange(String query) {
  listAdapter.filterData(query);
  expandAll();
  return false;
 }

 @Override
 public boolean onQueryTextSubmit(String query) {
  listAdapter.filterData(query);
  expandAll();
  return false;
 }
}

13 comments :

  1. how do you implement this functionality in devices that uses API lower than 8?. Since the widget SearchView is not supported?

    ReplyDelete
    Replies
    1. If you looking in Google (for example) "Action Bar Sherlock", you will find the answer.

      Ciao!

      Delete
  2. Thanks, this has helped me a lot.

    Ciao! Alberto

    ReplyDelete
  3. how to click on child items?

    ReplyDelete
  4. WonderFul Tutorial....Really very helpful...Thanku

    ReplyDelete
  5. Can you post a example how to do this with CursorTreeAdapter?

    ReplyDelete
  6. tenxs for tutorial. Is great. But a have a question. I need paint to child (expandableslistview). the code for paint is name.setPaintFlags(name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG ). But i need compare the code (IF) in the child tetxtview and this es equal. Sorry my english. Rewards

    ReplyDelete
  7. hi tnxs 4 tutorial
    where does the comparison happen in search, i want to add more search option to the program. tnxs for all.
    it's argent

    ReplyDelete
  8. Can you send a example how to do this with CursorTreeAdapter?
    My email: hemaramanakumar@gmail.com

    ReplyDelete