Filtering ListView with custom (object) adapter
You need to do a few things:
1) In your activity, register for a text change listener on your EditText that contains the value the user enters:
mSearchValue.addTextChangedListener(searchTextWatcher);
2) Create your searchTextWatcher and have it do something:
private TextWatcher searchTextWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // ignore } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // ignore } @Override public void afterTextChanged(Editable s) { Log.d(Constants.TAG, "*** Search value changed: " + s.toString()); adapter.getFilter().filter(s.toString()); } };
3) Override getFilter() in your custom adapter and have it filter the results and notify the listview that the dataset has changed.
@Override public Filter getFilter() { return new Filter() { @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { Log.d(Constants.TAG, "**** PUBLISHING RESULTS for: " + constraint); myData = (List<MyDataType>) results.values; MyCustomAdapter.this.notifyDataSetChanged(); } @Override protected FilterResults performFiltering(CharSequence constraint) { Log.d(Constants.TAG, "**** PERFORM FILTERING for: " + constraint); List<MyDataType> filteredResults = getFilteredResults(constraint); FilterResults results = new FilterResults(); results.values = filteredResults; return results; } }; }
Here an interesting example
public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { final FilterResults oReturn = new FilterResults(); final ArrayList<station> results = new ArrayList<station>(); if (orig == null) orig = items; if (constraint != null) { if (orig != null && orig.size() > 0) { for (final station g : orig) { if (g.getName().toLowerCase() .contains(constraint.toString())) results.add(g); } } oReturn.values = results; } return oReturn; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { items = (ArrayList<station>) results.values; notifyDataSetChanged(); } };}public void notifyDataSetChanged() { super.notifyDataSetChanged(); notifyChanged = true;}
For those who don't need the Filterable
interface, there is a much simpler solution. This also handles notifyDataSetChanged()
correctly where the other solutions fail. Note that you need to add a getArray()
function to the BaseAdapter
that just returns the array object that was passed to the constructor.
public abstract class BaseFilterAdapter<T> extends BaseAdapter<T> { private List<T> original; private String lastFilter; public BaseFilterAdapter(Context context, List<T> array) { super(context, new LinkedList<T>()); original = array; filter(""); } protected abstract Boolean predicate(T element, String filter); public void filter(String filter) { lastFilter = filter; super.getArray().clear(); for (T element : original) if (predicate(element, filter)) super.getArray().add(element); super.notifyDataSetChanged(); } @Override public List<T> getArray() { return original; } @Override public void notifyDataSetChanged() { filter(lastFilter); }}