I sub classed SimpleAdapter
to add some extra functionality to it, like changing background color, custom views and filtering. The background thing is working out great but the filter isn't. If I use the SimpleFilter
provided by the adapter there is no problem at all, so I copied the methods from the source and put them into my adapter. Although I didn't touch anything I'll get a IndexOutOfBoundsException
when typing in the search term. Usually on the second or third character.
I copied the whole class but the interesting part is the CustomFilter
bindView
and getView
is working great. After implementing the filter changed it stops working. The exception is caused by data.get(position) in the bindView
method on the first line but the problem has to be the filter.
public class ChangingColorAdapter extends SimpleAdapter {
int resource;
int[] to;
List<Map<String, String>> data, mUnfilteredData;
String[] from;
Context context;
Resources res;
Filter filter;
/*
* A custom adapter which changes background colors on the elements
* and implements custom filtering
*/
public ChangingColorAdapter(Context context,
List<Map<String, String>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
this.context = context;
this.resource = resource;
this.data = data;
this.from = from;
this.to = to;
this.res = context.getResources();
}
@Override
public Filter getFilter() {
if(filter != null) return filter;
else return filter = new CustomFilter();
}
/*
* A custom filter which is copied from the simplefilter in simpleadapter.
* Changed word check from .startswith to .contains
*/
private class CustomFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mUnfilteredData == null) {
mUnfilteredData = new ArrayList<Map<String, String>>(data);
}
if (prefix == null || prefix.length() == 0) {
List<Map<String, String>> list = mUnfilteredData;
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
List<Map<String, String>> unfilteredValues = mUnfilteredData;
int count = unfilteredValues.size();
ArrayList<Map<String, ?>> newValues = new ArrayList<Map<String, ?>>(count);
for (int i = 0; i < count; i++) {
Map<String, ?> h = unfilteredValues.get(i);
if (h != null) {
int len = to.length;
for (int j=0; j<len; j++) {
String str = (String)h.get(from[j]);
String[] words = str.split(" ");
int wordCount = words.length;
for (int k = 0; k < wordCount; k++) {
String word = words[k];
if (word.toLowerCase().contains(prefixString)) {
newValues.add(h);
break;
}
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
data = (List<Map<String, String>>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(resource, parent, false);
} else {
v = convertView;
}
bindView(position, v);
if(position%2!=0) {
v.setBackgroundColor(Color.DKGRAY);
}
else {
v.setBackgroundColor(Color.BLACK);
}
return v;
}
/*
* Iterate over all elements, check which type the given view is and fill in the data
*/
private void bindView(int position, View view) {
final Map<String, String> dataSet = data.get(position);
if (dataSet == null) {
return;
}
final ViewBinder binder = getViewBinder();
final String[] from = this.from;
final int[] to = this.to;
final int count = to.length;
for (int i = 0; i < count; i++) {
final View v = view.findViewById(to[i]);
if (v != null) {
final Object data = dataSet.get(from[i]);
String text = data == null ? "" : data.toString();
if (text == null) {
text = "";
}
boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, data, text);
}
if (!bound) {
if (v instanceof Checkable) {
if (data instanceof Boolean) {
((Checkable) v).setChecked((Boolean) data);
} else if (v instanceof TextView) {
setViewText((TextView) v, text);
} else {
throw new IllegalStateException(v.getClass().getName() +
" should be bound to a Boolean, not a " +
(data == null ? "<unknown type>" : data.getClass()));
}
} else if (v instanceof TermView) {
if(text.length()==10) {
int year = Integer.parseInt(text.substring(0,4));
if(text.substring(5, 7).equals("04")) {
setViewText((TextView) v, res.getString(R.string.summerterm) + " " + year);
}
else {
setViewText((TextView) v, res.getString(R.string.winterterm) + " " + year + "/" + (year+1));
}
}
else {
setViewText((TextView) v, text);
}
} else if (v instanceof TextView) {
setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
if (data instanceof Integer) {
setViewImage((ImageView) v, (Integer) data);
} else {
setViewImage((ImageView) v, text);
}
} else {
throw new IllegalStateException(v.getClass().getName() + " is not a " +
" view that can be bounds by this SimpleAdapter");
}
}
}
}
}
}