Android Filter Custom ListView (BaseAdapter)

By | August 23, 2014

In the previous tutorial we have seen an example of how to filter Simple Listview which uses ArrayAdapter , when it comes to custom listview adding search filter it’s typically different not so easy as simple listview because in custom listview each row contains many data (also termed as constrains) , to filter custom listview you need to create your own custom filter for a constrains by which you can filter it

Before I start coding i kept all the necessary images inside the drawable folder and also declared array of string inside value.xml which you can find inside the download code link below .

If you not familiar with custom listview then check out this previous tutorial , Custom listview using basedapter .

XML Layout

Create activity_main.xml file inside layout directory and add the SearchView and ListView to it .

file :activity_main.xml

<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:padding=”10dp”
    tools:context=”.MainActivity” >

    <!– Editext for Search –>

    <SearchView
        android:id=”@+id/search_view”
        android:layout_width=”match_parent”
        android:layout_height=”wrap_content”
        android:background=”@drawable/search_view_border”
        android:iconifiedByDefault=”false”
        android:padding=”2dp”
        android:queryHint=”Search….” />

     <!– ListView –>

    <ListView
        android:id=”@+id/list_view”
        android:layout_width=”match_parent”
        android:layout_height=”match_parent” />

</LinearLayout>

Create list_items.xml , add ImageView and two TextView to it , this xml layout represents the row’s of ListView.

file :list_item.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout 
    xmlns:android=”http://schemas.android.com/apk/res/android”
    android:layout_width=”fill_parent”
    android:layout_height=”fill_parent”
    android:padding=”2dp” >

    <ImageView
        android:id=”@+id/flag”
        android:layout_width=”80dp”
        android:layout_height=”70dp”
        android:paddingLeft=”10dp”
        android:paddingRight=”10dp” />

    <TextView
        android:id=”@+id/name”
        android:layout_width=”wrap_content”
        android:layout_height=”wrap_content”
        android:layout_marginLeft=”10dp”
        android:layout_marginTop=”10dp”
        android:layout_toRightOf=”@+id/flag”
        android:paddingBottom=”10dp”
        android:text=”txt”
        android:textColor=”#000000″
        android:textSize=”20sp” />

    <TextView
        android:id=”@+id/code”
        android:layout_width=”wrap_content”
        android:layout_height=”wrap_content”
        android:layout_alignLeft=”@+id/name”
        android:layout_below=”@+id/name”
        android:layout_marginLeft=”5dp”
        android:text=”txt”
        android:textColor=”#000000″
        android:textSize=”16sp” />

</RelativeLayout>

Java Bean

Create a Bean Class Model which is used for setting and getting row data’s of each items of ListView.

file :Country.java

package com.pavan.customfilter;

public class Country {

    String name;
    String iso_code;
    int flag;

    Country(String name, String iso_code, int flag) {
        this.name = name;
        this.iso_code = iso_code;
        this.flag = flag;
    }

    public String getIso_code() {
        return iso_code;
    }

    public void setIso_code(String iso_code) {
        this.iso_code = iso_code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }

}

Custom Adapter and Custom Filter

Inside the getView() method inflate the items of listview with above defined list_item.xml layout .

Custom Filter 
We have seen in previous example of filtering simple listview where we used default  filter , when it comes to custom listview we need custom filter the reason for this is items of custom listview is associated with many data (which we call it as constrains) so we need to decide on which constrain the listview should be filtered .

How to Create Custom filter

  1. Implement the Filterable Interface and override the getFilter() method .
  2. Create  Inner Class ValueFilter and extend it to Filter Class .
  3. Now Override performFiltering() and publishResult() inside ValueFilter Class .
  4. Inside Create FilterResults Object and write a logic on which constrain you would like to filter listview (here in this example the listview is filter by country name so here country is checked with the constraint) .
  5. The PublishResults method is used for notifying change in data set .

file :CustomAdapter.java

package com.pavan.customfilter;

import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;

public class CustomAdapter extends BaseAdapter implements Filterable {

    Context context;
    ArrayList<Country> countrylist;
    ArrayList<Country> mStringFilterList;
    ValueFilter valueFilter;

    CustomAdapter(Context context , ArrayList<Country> countrylist) {
        this.context = context;
        this.countrylist = countrylist;
        mStringFilterList = countrylist;
    }

    @Override
    public int getCount() {
        return countrylist.size();
    }

    @Override
    public Object getItem(int position) {
        return countrylist.get(position);
    }

    @Override
    public long getItemId(int position) {
        return countrylist.indexOf(getItem(position));
    }

    @Override
    public View getView(int position , View convertView , ViewGroup parent ) {

        LayoutInflater mInflater = (LayoutInflater) context
                .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);

        convertView = null;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item, null);

            TextView name_tv = (TextView) convertView.findViewById(R.id.name);
            TextView iso_tv = (TextView) convertView.findViewById(R.id.code);
            ImageView iv = (ImageView) convertView.findViewById(R.id.flag);

            Country country = countrylist.get(position);

            name_tv.setText(country.getName());
            iso_tv.setText(country.getIso_code());
            iv.setImageResource(country.getFlag());
        }
        return convertView;
    }

    @Override
    public Filter getFilter() {
        if (valueFilter == null) {
            valueFilter = new ValueFilter();
        }
        return valueFilter;
    }

    private class ValueFilter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();

            if (constraint != null && constraint.length() > 0) {
                ArrayList<Country> filterList = new ArrayList<Country>();
                for (int i = 0; i < mStringFilterList.size(); i++) {
                    if ( (mStringFilterList.get(i).getName().toUpperCase() )
                            .contains(constraint.toString().toUpperCase())) {

                        Country country = new Country(mStringFilterList.get(i)
                                .getName() ,  mStringFilterList.get(i)
                                .getIso_code() ,  mStringFilterList.get(i)
                                .getFlag());

                        filterList.add(country);
                    }
                }
                results.count = filterList.size();
                results.values = filterList;
            } else {
                results.count = mStringFilterList.size();
                results.values = mStringFilterList;
            }
            return results;

        }

        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results) {
            countrylist = (ArrayList<Country>) results.values;
            notifyDataSetChanged();
        }

    }

}

Main Activity

  • Create MainActivity.java and extend this class to activity class and set the content of this activity to above defined activity_main.xml file .
  • Setup the ListView with above defined baseadapter .
  • Set onQueryTextListeners for SearchView , to set this you need to implement SearchView.OnQueryTextListener interface for your class and override the two methods of it , they are onQueryTextChange and onQueryTextSubmit .
  • Now inside the onQueryTextChange method call getFilter().filter(newText) on adapter object by passing your search string .

file :MainActivity.java

package com.pavan.customfilter;

import java.util.ArrayList;
import android.app.Activity;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SearchView;

public class MainActivity extends Activity implements
        SearchView.OnQueryTextListener {

    ListView lv;
    SearchView search_view;

    String[] country_names , iso_codes ;
    TypedArray country_flags ;

    ArrayList<Country> countrylist ;
    CustomAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lv = (ListView) findViewById(R.id.list_view);
        search_view = (SearchView) findViewById(R.id.search_view);

        country_names = getResources().getStringArray(R.array.country_names);
        iso_codes = getResources().getStringArray(R.array.iso_Code);
        country_flags = getResources().obtainTypedArray(R.array.country_flags);

        countrylist = new ArrayList<Country>();
        for (int i = 0; i < country_names.length; i++) {
            Country country = new Country(country_names[i] , iso_codes[i] ,
                    country_flags.getResourceId(i, -1) );
            countrylist.add(country);
        }

        adapter = new CustomAdapter(getApplicationContext(), countrylist );
        lv.setAdapter(adapter);

        search_view.setOnQueryTextListener(this);
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        adapter.getFilter().filter(newText);
        return false;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }

}

  • Good nice Example………
    pls post android projects…………………

  • Good example, pls learn the working of Filter interface methods & FilterResults obj in details beginners.

  • it don't work with fragment.
    Please tutorial.
    tks.

  • Good tutorial bro……

  • 😀 (y)

  • Nice tutorial… Good work (Y)

  • Many thanks for this – finally got a searchview that works properly – not just using startsWith()

  • Great tutorial, thanks! Suggested edit to simplify (and speed up) .performFiltering()

    protected FilterResults performFiltering(CharSequence constraint) {
    FilterResults results = new FilterResults();
    if (constraint == null || constraint.length() = 0) {
    results.count = mStringFilterList.size();
    results.values = mStringFilterList;
    } else {
    String searchStr = constraint.toString.toUppercase();//do this once.
    ArrayList filterList = new ArrayList();
    for (Country country : mStringFilterList)
    if (country.getName().toUpperCase().contains(searchStr) {
    filterlist.add(country);}
    results.count = filterList.size();
    results.values = filterList;
    }
    return results;
    }

  • Great Tutorial, thanks. Suggested edit to greatly simplify (and speed up) .performFiltering()

    if(constraint==null||constraint.length()=0)
    {
    results.count = mStringFilterList.size();
    results.values = mStringFilterList;
    } else {
    String searchStr=constraint.toString.toUppercase();//do this once.
    ArrayList filterList = new ArrayList();
    for (Country country: mStringFilterList)
    if (country.getName().toUpperCase().contains(searchStr) filterlist.add(country);
    results.count = filterList.size();
    results.values = filterList;
    }
    return results;

  • But when you implement onItemSelectListener on Listview what should be the position ?
    How to implement that functionality

  • @Tausifali Saiyed
    Yes you canimplement onItemSelectListener on Items of Listview

  • Great post. Easy to implement & understand.

  • so may thanks for you.. i

  • Very very good the example.
    I'm new in developer android and understood easy

  • i got an error that cannot resole getFilter(). Please help me to fix this

  • Hola , estoy implementando tu ejemplo y no me sale error en nada pero no me filtra el resultado, me podrias ayudar??

  • Thank you