Android ListView Custom Filter and Filterable interface

This post describes how to use Android Listview custom filter. In the previous post we showed the way we can programming a custom adapter and a custom layout. One aspect we didn’t cover by now, is how we can filter the items in the ListView. In this post we describe how we can develop an android listview custom filter to filter items inside a ListView while the user writes words inside an input field. We can use the example shown in the last post and modify it. What do we need to do it?

  • Modify the custom adapter, PlanetAdapter so that it will be able to filter the items
  • Add an input field where the user can insert the keys to filter the ListView

Let’s start then.

Android ListView Custom filter

Modify the custom adapter so that it implements the Filterable interface

If we didn’t use a custom adapter witha  custom object inside the ListView, the things would be much easier. For example, if we used just String as item content we simply could add a few lines of code to make our list filterable; but we have a custom layout that uses not a simple String but a complex object (i.e. Planet). So we need first modify our custom adapter (PlanetAdapter) so that it implements the android.widget.Filter interface. This interface requires we implement just only one method called getFilter() that returns a android.widget.Filter object. The problem is the way we can create this filter. Very easy!

private class PlanetFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
....
}

@Override
protected void publishResults(CharSequence constraint,FilterResults results) {
.....
}

}

To enable Android Listview custom filter, we create a private class called PlanetFilter that extends the android.widget.Filter. This class has two abstract methods:

  • FilterResults performFiltering(CharSequence constraint) : invoked in worker thread, that has the task to filter the results according to the constraint
  • void publishResults(CharSequence constraint,FilterResults results): that has the task to show the result set created by performingFiltering method

Let’s suppose we want to filter the planet list using the name attribute. In our case, then, the constraint can be the initial letters of the planet name, so that if the planet starts with those letters we show it otherwise we hide it.

@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
// We implement here the filter logic
if (constraint == null || constraint.length() == 0) {
// No filter implemented we return all the list
results.values = planetList;
results.count = planetList.size();
}
else {
// We perform filtering operation
List<Planet> nPlanetList = new ArrayList<Planet>();

for (Planet p : planetList) {
if (p.getName().toUpperCase().startsWith(constraint.toString().toUpperCase()))
nPlanetList.add(p);
}

results.values = nPlanetList;
results.count = nPlanetList.size();

}
return results;
}

In this method, we have to return a FilterResults that has two static attributes called values and count. Values is a list of values to be shown while count is the number of these values. The first thing we do in this method is to verify if the constraint is null or empty, in this case we don’t have to filter the result and simply

results.values = planetList;
results.count = planetList.size();

In other words all the list. If the constraints aren’t null or empty we simply select the planet with its name starting with the constraint, as shown below.

List<Planet> nPlanetList = new ArrayList<Planet>();

for (Planet p : planetList) {
if (p.getName().toUpperCase().startsWith(constraint.toString().toUpperCase()))
nPlanetList.add(p);
}

results.values = nPlanetList;
results.count = nPlanetList.size();

The next step is publishing the result.

@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {

// Now we have to inform the adapter about the new list filtered
if (results.count == 0)
notifyDataSetInvalidated();
else {
planetList = (List<Planet>) results.values;
notifyDataSetChanged();
}

}

We said before that the PlanetAdapter has to implement Filterable interface and it has to implement getFilter() method:

@Override
public Filter getFilter() {
if (planetFilter == null)
planetFilter = new PlanetFilter();

return planetFilter;
}

This is code is quite trivial, we simply invalidate the result set if the result list is empty or we notify that the result set is changed.

It is done!….quite done!…We need now adding just an input field where user can insert the keyword to filter the result. The new layout becomes:

<LinearLayout 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:orientation="vertical"
android:focusable="true"
android:focusableInTouchMode="true">

<EditText
android:id="@+id/editTxt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:maxLines="1" />

<ListView android:id="@+id/listView"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"/>

<Button android:id="@+id/addBtn"
android:text="Add planet"
android:onClick="addPlanet"
android:layout_weight="0.5"
android:layout_height="80dp"
android:layout_width="match_parent"/>

</LinearLayout>

In the main activity we have to add some listener attached to EditText so that when user writes inside it we can filter the list items.

editTxt.addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
System.out.println("Text ["+s+"]");
aAdpt.getFilter().filter(s.toString());
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {

}

@Override
public void afterTextChanged(Editable s) {
}
});

Running the code we have:

     androdi_listview_filter android_listview_filtered_itmes

Tricks about Android listview custom filter

One simple trick we have to use is to make soft keyboard hidden at the app startup. As soon as the app starts the EditText gains the focus and the OS shows the keyboard covering the list items. If we don’t want this we can simply “move” the focus somewhere else:

android:focusable="true"
android:focusableInTouchMode="true"

Source code here

57 Comments

  1. Ederson Schmidt November 6, 2012
  2. Francesco Azzola November 6, 2012
  3. Francesco Azzola November 6, 2012
    • Ederson Schmidt November 7, 2012
  4. Francesco Azzola November 7, 2012
  5. Anonymous November 22, 2012
  6. Francesco Azzola November 22, 2012
  7. Anonymous December 11, 2012
    • Francesco Azzola December 11, 2012
    • Anonymous December 11, 2012
    • Anonymous December 11, 2012
  8. Francesco Azzola December 11, 2012
    • Anonymous December 11, 2012
    • Francesco Azzola December 11, 2012
    • Anonymous December 14, 2012
    • Anonymous December 14, 2012
    • Anonymous December 14, 2012
    • Francesco Azzola December 16, 2012
    • Anonymous December 18, 2012
  9. Anonymous December 22, 2012
  10. Francesco Azzola January 19, 2013
  11. imran khan February 25, 2013
  12. Anonymous March 12, 2013
  13. Anonymous2 May 3, 2013
  14. Mick Hearne May 4, 2013
  15. survivingwithandroid May 4, 2013
  16. Mick Hearne May 4, 2013
  17. survivingwithandroid May 4, 2013
  18. Mick Hearne May 4, 2013
  19. dentex June 17, 2013
  20. survivingwithandroid June 18, 2013
  21. dentex June 18, 2013
  22. dentex June 18, 2013
  23. dentex June 18, 2013
  24. survivingwithandroid June 18, 2013
  25. dentex June 18, 2013
  26. dentex June 18, 2013
  27. Rohit June 28, 2013
  28. survivingwithandroid July 1, 2013
  29. Rohit July 2, 2013
  30. dentex August 26, 2013
  31. Rontrile November 17, 2013
  32. Estuardo León December 4, 2013
  33. jaffar April 1, 2014
  34. Aryanata April 23, 2014
  35. pkpdeveloper May 11, 2014
  36. Deepak D July 2, 2014
  37. Deepak D July 4, 2014
  38. Ersin July 28, 2014
  39. Pagio August 7, 2014
  40. Jorge Casariego September 16, 2014
  41. kelvin October 4, 2014
  42. anurag February 15, 2015
  43. Junior Frogie March 23, 2015
  44. Sami June 4, 2015
  45. NaseemH July 3, 2015

Add Comment