Skip to content

Instantly share code, notes, and snippets.

@fjfish
Created June 30, 2012 15:48
Show Gist options
  • Save fjfish/3024308 to your computer and use it in GitHub Desktop.
Save fjfish/3024308 to your computer and use it in GitHub Desktop.
Simple String Adapter for Android ListView that has a filter that gives whatever it finds and ignores word boundaries
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp" >
</TextView>
package com.yourco.yourapp;
import java.util.ArrayList;
import java.util.List;
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.TextView;
// The standard text view adapter only seems to search from the beginning of whole words
// so we've had to write this whole class to make it possible to search
// for parts of the arbitrary string we want
public class SearchableAdapter extends BaseAdapter implements Filterable {
private List<String>originalData = null;
private List<String>filteredData = null;
private LayoutInflater mInflater;
private ItemFilter mFilter = new ItemFilter();
public SearchableAdapter(Context context, List<String> data) {
this.filteredData = data ;
this.originalData = data ;
mInflater = LayoutInflater.from(context);
}
public int getCount() {
return filteredData.size();
}
public Object getItem(int position) {
return filteredData.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.list_view);
// Bind the data efficiently with the holder.
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// If weren't re-ordering this you could rely on what you set last time
holder.text.setText(filteredData.get(position));
return convertView;
}
static class ViewHolder {
TextView text;
}
public Filter getFilter() {
return mFilter;
}
private class ItemFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final List<String> list = originalData;
int count = list.size();
final ArrayList<String> nlist = new ArrayList<String>(count);
String filterableString ;
for (int i = 0; i < count; i++) {
filterableString = list.get(i);
if (filterableString.toLowerCase().contains(filterString)) {
nlist.add(filterableString);
}
}
results.values = nlist;
results.count = nlist.size();
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredData = (ArrayList<String>) results.values;
notifyDataSetChanged();
}
}
}
@reixa00
Copy link

reixa00 commented Mar 7, 2014

Helped me a lot. Thank you very much!

@mkaarthick
Copy link

Thanks! In my case I want to pass its contents to next activity, so I did something like this
Intent i = new Intent(FirstActivity.this,SecondActivity.class);
i1.putExtra("main_title", filteredData.get(position));
i1.putExtra("sub_title", subTitle.get(position));
startActivity(i);
While filtering, 'main_title' is passing correctly to next activity, but 'sub_title' remains the same for all list items. How can I do the same for sub title?

@narenmagar
Copy link

I used your method but didn't any solution.please help me?
public class AddsAdapter extends ArrayAdapter implements Filterable
{

private LayoutInflater inflater;
public String imgstore="http://office.eastlinkhost.com/TrendMedia/banner/";
public static String imgurl="";
//public ImageLoader imageLoader; 
private RequestQueue mRequestQueue;
private ImageLoader imageLoader;
private ItemFilter mFilter = new ItemFilter();
private List<addsinfo> originalData = null;
private List<addsinfo> filteredData = null;


public AddsAdapter(Context context,List<addsinfo> newsadapter) 
{
    super(context,R.layout.adds_listview,newsadapter);
    // TODO Auto-generated constructor stub
    inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    //imageLoader=new ImageLoader(context.getApplicationContext());
    // TODO Auto-generated constructor stub
    this.filteredData = newsadapter ;
    this.originalData = newsadapter ;
    //mInflater = LayoutInflater.from(context);
}


public View getView(int postion,View contentView,ViewGroup parent)
{
    View item=inflater.inflate(R.layout.view_all_listview,parent,false);



    //TextView tv1=(TextView) item.findViewById(R.id.title);
    TextView tv2=(TextView) item.findViewById(R.id.textView3);
    TextView tv1=(TextView) item.findViewById(R.id.textView4);
    ImageView img=(ImageView) item.findViewById(R.id.list_image);
    //img.setBackgroundResource(R.anim.animations);
    //AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();

     // Start the animation (looped playback by default).
     //frameAnimation.start();

    //creating the object of the student
    addsinfo latestnews=getItem(postion);
    //Log.d("latestnews", latestnews.ge());

    //populate the custom list view with the class of Student
    tv1.setText(latestnews.getVendor());
    tv2.setText(latestnews.getPrice());

   String image=latestnews.getImg();

    imgurl=imgstore+image;
    Log.d("imageurl", ""+imgurl);



    //imageLoader.DisplayImage(imgurl, img);


    img.setScaleType(ScaleType.FIT_XY);

    //new DisplayImageFromURL((ImageView) item.findViewById(R.id.list_image)).execute(imgurl);

    //mRequestQueue = Volley.newRequestQueue(getContext());
    //imageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache(
    //      BitmapLruCache.getDefaultLruCacheSize()));


    //imageLoader.get(imgurl, ImageLoader.getImageListener(img,R.drawable.loading, //R.drawable.loading));

    return item;    
}

public Filter getFilter()
{
return mFilter;
}

private class ItemFilter extends Filter
{
@OverRide
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
final List list = originalData;

    int count = list.size();
     final ArrayList<addsinfo> nlist = new ArrayList<addsinfo>(count);

    String filterableString ;
    for (int i = 0; i < count; i++) 
    {
         filterableString = list.get(i);
    if (filterableString.toLowerCase().contains(filterString))
      {
       nlist.add(filterableString);
      }
     }
     results.values = nlist;
    results.count = nlist.size();

     return results;

}

    @SuppressWarnings("unchecked")
  @Override
  protected void publishResults(CharSequence constraint, FilterResults results) 
    {
     filteredData = (List<addsinfo>) results.values;
     notifyDataSetChanged();
     }

}
}

And in my MainActivity.java I used

private TextWatcher filterTextWatcher = new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        adapter.getFilter().filter(s.toString());

// adapter.notifyDataSetChanged();
// listofFriends.setAdapter(adapter);
}

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        // TODO Auto-generated method stub

    }

    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub

    }
};

@altoin
Copy link

altoin commented Jul 17, 2014

Hello your code helped me, but please how do i pass content from the filtered list to another activity? The onItemClick listener of the lisview still pass the original data,not the filtered one. Thanks

@sergioabril
Copy link

Hi, is it working? I might been using it in a wrong way.
Any help would be appreciated.

I'm calling it this way:

List datas = new ArrayList();
datas.add("France");
datas.add("localia");
datas.add("Rancia");

SearchableAdapter adapter = new SearchableAdapter(this, datas);
AddressBar.setAdapter(adapter);

But I'm getting:

07-18 02:01:38.160: E/AndroidRuntime(30618): java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
07-18 02:01:38.160: E/AndroidRuntime(30618): at SearchableAdapter.getCount(SearchableAdapter.java:42)

@sergioabril
Copy link

For some strange reason filteredData was null sometimes, causing the crash.
I solved changing this:

public int getCount() {
if(filteredData==null){
Log.v("LOG","Warn, null filteredData");
return 0;
}else{
return filteredData.size();
}
}

@stasklev
Copy link

Helped me a lot! thanks :)

@rupomkhondaker
Copy link

my list comes in a fragment. how can i implement this method there

@ganesh76
Copy link

your search functionality is working fine but your code makes list view empty when we remove all characters in search field. it should load all the default data again

@calebklc
Copy link

Thank you! This helped me a lot!

@huSSooxXx
Copy link

Thank you.. And another thanks to "eiprol" for editing

@prathibhaprabs
Copy link

prathibhaprabs commented Apr 25, 2017

Amazing!!! A small edit,
public int getCount() {
return filteredData == null ? 0 : filteredData.size();
}

Perfect now :)
But when I do BackSpace, it is sorting until it has at least one character. On deleting the entire data, drop down is not shown. and when I click it again drop down is not coming.

WHYYYYYYYYYYY???????

@prathibhaprabs
Copy link

@ganesh76 How do we show the default list when text is removed. Do you have any solution for this?

@maxmiliano
Copy link

@prathibhaprabs You must update the results.values with the reference to the original data.
I've done this with the code below, before the for:

        if (filterString.isEmpty()) {
            results.values = list;
            results.count = list.size();
            return results;
        }

@trifagabriel
Copy link

Thank you!!

@brunoperezm
Copy link

Thanks a lot!! This helped me so much!

@nithinkolekar
Copy link

Thanks
[simple star or follow button could have omitted this unwanted comment]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment