I have a ListView using a custom ArrayList adapter - what's the best way to implement filtering ? Anyone have a example code to study?
Subject says it all. I have seen examples implementing a custom Filter. The Android developer docs talk about implementing a Filterable interface. Does anyone have any advice and/or sample code on the best way to implement filtering开发者_如何学C in a ListView ?
This video comes from the latest Google I/O (2010), it's called "The world of ListView".
http://code.google.com/intl/it-IT/events/io/2010/sessions/world-of-listview-android.html
at 34:25 it explains how to implement a text filter
here, near line 437, an example of use:
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ArrayAdapter.java
enjoy :)
Since this was the first answer I have found on google I decided to post some code to save the next person some time. I have come up with the code thanks to this blog: http://www.mokasocial.com/2010/07/arrayadapte-filtering-and-you/
public void buildSearchList {
lv2 = new ListView(this);
edi = new EditText(this);
edi.setHint(R.string.teclear);
edi.addTextChangedListener(filterTextWatcher);
lv2.addFooterView(cancelButton);
lv2.addHeaderView(edi);
lv2.setAdapter(mAdapter2);
lv2.setTextFilterEnabled(true);
}
private class EventAdapter extends ArrayAdapter<Articulo> implements
Filterable {
public ArrayList<Articulo> mEvents = null;
private final Object mLock = new Object();
private Filter filter;
public EventAdapter(Context c, ArrayList<Articulo> clientes) {
super(c, android.R.layout.test_list_item);
mContext = c;
mEvents = clientes;
filter = new MyFilter();
}
@Override
public Filter getFilter() {
if (filter == null) {
filter = new MyFilter();
}
return filter;
}
public int getCount() {
return mEvents.size();
}
public Articulo getItem(int position) {
return mEvents.get(position);
}
public long getItemId(int position) {
return mEvents.get(position).getIdCodigo();
}
public View getView(int position, View convertView, ViewGroup parent) {
EventEntryView btv;
if (convertView == null) {
btv = new EventEntryView(mContext, mEvents.get(position));
} else {
btv = (EventEntryView) convertView;
String title1 = mEvents.get(position).getDescripcion();
if (title1 != null) {
btv.setText1Title(title1);
}
}
btv.setBackgroundColor(Color.BLACK);
return btv;
}
private Context mContext;
private class MyFilter extends Filter {
protected FilterResults performFiltering(CharSequence prefix) {
// Initiate our results object
FilterResults results = new FilterResults();
// Collection<? extends Articulo> mItemsArray = null;
// If the adapter array is empty, check the actual items array
// and use it
if (mEvents == null) {
synchronized (mLock) { // Notice the declaration above
if(cual==1)
mEvents = new ArrayList<Articulo>(clientes);
else mEvents = new ArrayList<Articulo>(ventas);
}
}
// No prefix is sent to filter by so we're going to send back
// the original array
if (prefix == null || prefix.length() == 0) {
synchronized (mLock) {
if(cual==1){
results.values = clientes;
results.count = clientes.size();
}else {
results.values = ventas;
results.count = ventas.size();
}
}
} else {
// Compare lower case strings
String prefixString = prefix.toString().toLowerCase();
// Local to here so we're not changing actual array
final ArrayList<Articulo> items = mEvents;
final int count = items.size();
final ArrayList<Articulo> newItems = new ArrayList<Articulo>(
count);
for (int i = 0; i < count; i++) {
final Articulo item = items.get(i);
final String itemName = item.getDescripcion()
.toString().toLowerCase();
// First match against the whole, non-splitted value
if (itemName.startsWith(prefixString)) {
newItems.add(item);
} else {
// else {} // This is option and taken from the
// source of
// ArrayAdapter
final String[] words = itemName.split(" ");
final int wordCount = words.length;
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newItems.add(item);
break;
}
}
}
}
// Set and return
results.values = newItems;
results.count = newItems.size();
}
return results;
}
@SuppressWarnings("unchecked")
protected void publishResults(CharSequence prefix,
FilterResults results) {
// noinspection unchecked
mEvents = (ArrayList<Articulo>) results.values;
// Let the adapter know about the updated list
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
private class EventEntryView extends LinearLayout {
private TextView text1;
public EventEntryView(Context context, Articulo subSolicitud) {
super(context);
this.setOrientation(VERTICAL);
text1 = new TextView(context);
text1.setTextSize(20);
text1.setPadding(10, 20, 10, 20);
text1.setTextColor(Color.WHITE);
String t = subSolicitud.getDescripcion();
text1.setText(t);
addView(text1, new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
public void setText1Title(String title1) {
text1.setText(title1);
}
}
private TextWatcher filterTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
mAdapter2.getFilter().filter(s);
}
};
There are two possible ways of Resolving this
1. Use your own Filtering Algorithm to filter the adapter(As said by others). 2. The second and much simpler method is to override the tostring method in the Custom RowItem class you might have defined
@Override
public String toString() {
return name + "\n" + description;
}
where name and description are the possible text you have stored in the row items on which you want filtering
and use the adapter.getFilter().filter(s); as such you were using it will work now because your adapter now returns a valid string to filter
I looked at some sample code from other developers and learned a lot by simply reading through the source for ArrayAdapter. Armed with that info I managed to implement my own filtering.
精彩评论