ListView是常用的列表显示控件,而ListView显示数据就需要ListAdapter实现类对象的支持,下面是一张前面文章中的插图,这张图可以看出ListView是需要ListAdapter对象的,也可以看到ListAdapter接口的实现层次。Android提供的实现类有两种,其一SimpleAdapter,ArrayAdapter,CursorAdapter都继承自BaseAdapter,其二为HeaderViewListAdapter,他们都实现了Filterable接口。
我们实现自定义Adapter类时,可以直接从ListAdapter实现,也可以从BaseAdapter开始继承,当然BaseAdapter也可以适用于对Spinner控件的Adapter设置,另外,也要注意Filterable接口的使用。
下面我们的介绍主要沿着Adapter<-ListAdapter<-BaseAdapter这条线进行说明,末尾加上对Filterable的说明。
第一,Adapter接口定义的方法
1.int getCount():获取列表元素长度;
2.Object getItem(int position):获取给定位置的列表元素;
3.int getItemViewType(int position):获取列表视图单元类型
4.View getView(int position, View convertView, ViewGroup parent):这个是Adapter对象的核心方法,用来获取列表视图单元,返回一个视图,填充到列表指定位置position。
5.int getViewTypeCount():获取列表视图类型种类数;
6.boolean hasStableIds()
7.boolean isEmpty():判断列表是否为空;
8.void registerDataSetObserver(DataSetObserver observer)
9.void unregisterDataSetObserver(DataSetObserver observer)
第二,ListAdapter新增的方法:列表项获取enable属性方法
1. boolean areAllItemsEnabled()
2. boolean isEnabled(int position)
第三,BaseAdapter新增的方法
1.View getDropDownView(int position, View convertView, ViewGroup parent):这个方法是实现自SpinnerAdapter接口的方法,这里不做介绍。
2.void notifyDataSetChanged()
3.void notifyDataSetInvalidate()
对于2,3这两个方法,在BaseAdapter中的源码如下,这两个是代理方法;方法2是为了实现在数据变动时刷新视图显示的;方法3执行的结果使得Adapter对象不再可用,也不可以再添加数据更新。
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
关于Filterable的介绍
Filterable接口只定义了一个方法Filter getFilter();这个方法返回一个用于文字处理的Filter子类对象,在Android 提供的API中找不到抽象类Filter的实现类,也就是说,你必须自己实现一个Filter子类,进行数据处理。下面贴出ArrayAdapter中实现的Filter子类ArrayFilter的源码,可以作为参考示例:
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}