Adapter的作用。Adapter是AdapterView视图与数据之间的桥梁,Adapter提供对数据的访问,也负责为每一项数据产生一个对应的View。其作用如下图所示:
|
|
Adapter
Adapter做为这个继承结构的最顶层的基接口,定义了Adapter要实现的基本方法:
view sourceprint?01public interface Adapter {
02//注册一个Observer,当Adapter所表示的数据改变时会通知它,DataSetObserver是一个抽象类,定义了两个方法:onChanged与onInvalidated
03void registerDataSetObserver(DataSetObserver observer);
04//取消注册一个Observer
05void unregisterDataSetObserver(DataSetObserver observer);
06//所表示的数据的项数
07int getCount();
08//返回指定位置的数据项
09Object getItem(int position);
10//返回指定位置的数据项的ID
11long getItemId(int position);
12//表示所有数据项的ID是否是稳定的,在BaseAdapter中默认返回了false,假设是不稳定的,在CursorAdapter中返回了true,Cursor中的_ID是不变的
13boolean hasStableIds();
14//为每一个数据项产生相应的视图
15View getView(int position, View convertView, ViewGroup parent);
16//为了避免产生大量的View浪费内存,在Android中,AdapterView中的View是可回收的使用的。比如你有100项数据要显示,而你的屏幕一次只能显示10条数据,则
17//只产生10个View,当往下拖动要显示第11个View时,会把第1个View的引用传递过去,更新里面的数据再显示,也就是说View可重用,只是更新视图中的数据用于显示新
18//的一项,如果一个视图的视图类型是IGNORE_ITEM_VIEW_TYPE的话,则此视图不会被重用
19static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
20//获得相应位置的这图类型
21int getItemViewType(int position);
22//getView可以返回的View的类型数量。(在HeaderViewListAdapter中可以包含Header和Footer,getView可以返回Header、Footer及Adapter
23//中的视图,但其getViewTypeCount的实现只是调用了内部Adapter的的getViewTypeCount,忽略了Header、Footer中的View Type,不懂。
24int getViewTypeCount();
25static final int NO_SELECTION = Integer.MIN_VALUE;
26boolean isEmpty();
27}
Adapter有两个子接口,ListAdapter(列表)与SpinnerAdapter(下拉列表),它们都只定义了少数方法。一般除WrapperListAdapter接口及其实现类只实现了ListAdapter外,都同时实现了这两个接口。
ListAdapter
view sourceprint?1//是否在ListAdapter中的所有项都enabled,即是否所有项都selectable和clickable
2public boolean areAllItemsEnabled();
3//指定位置的项是否是enabled的
4boolean isEnabled(int position);
SpinnerAdapter
view sourceprint?1//产生相应位置下拉项的视图
2public View getDropDownView(int position, View convertView, ViewGroup parent);
BaseAdapter
view sourceprint?1一个抽象类,Adapter的基础实现类,一般作为其他实现类的基类,同时实现ListAdapter与SpinnerAdapter,提供了一些方法的默认实现:
view sourceprint?01public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
02//提供一些方法,当数据改变时调用注册的DataSetObserver的回调函数
03private final DataSetObservable mDataSetObservable = new DataSetObservable();
04public boolean hasStableIds() {
05return false;
06}
07public void registerDataSetObserver(DataSetObserver observer) {
08mDataSetObservable.registerObserver(observer);
09}
10public void unregisterDataSetObserver(DataSetObserver observer) {
11mDataSetObservable.unregisterObserver(observer);
12}
13//通知相关联的视图,相应的数据已经改变
14public void notifyDataSetChanged() {
15mDataSetObservable.notifyChanged();
16}
17public void notifyDataSetInvalidated() {
18mDataSetObservable.notifyInvalidated();
19}
20public boolean areAllItemsEnabled() {
21return true;
22}
23public boolean isEnabled(int position) {
24return true;
25}
26//通过getView实现
27public View getDropDownView(int position, View convertView, ViewGroup parent) {
28return getView(position, convertView, parent);
29}
30public int getItemViewType(int position) {
31return 0;
32}
33public int getViewTypeCount() {
34return 1;
35}
36public boolean isEmpty() {
37return getCount() == 0;
38}
39}
ArrayAdapter
view sourceprint?1以下是SDK的说明:
A concrete BaseAdapter that is backed by an array of arbitrary objects. By default this class expects that the provided resource id references a single TextView. If you want to use a more complex layout, use the constructors that also takes a field id. That field id should reference a TextView in the larger layout resource.
However the TextView is referenced, it will be filled with the toString() of each object in the array. You can add lists or arrays of custom objects. Override the toString() method of your objects to determine what text will be displayed for the item in the list.
To use something other than TextViews for the array display, for instance, ImageViews, or to have some of data besides toString() results fill the views, override getView() to return the type of view you want.
默认ArrayAdapter产生的视图期望一个TextView,或者也可以指定一个Layout并指定其中一个类型为TextView的资源的ID,其底下的数据可以是任意类型,但显示的时候会调用其toString()方法,也就是说只能用TextView显示文字,如果想显示其他的数据,要重写getView()
ArrayAdapter有5个构造函数
view sourceprint?01public ArrayAdapter(Context context, int textViewResourceId) {
02init(context, textViewResourceId, 0, new ArrayList());
03}
04public ArrayAdapter(Context context, int resource, int textViewResourceId) {
05init(context, resource, textViewResourceId, new ArrayList());
06}
07public ArrayAdapter(Context context, int textViewResourceId, T[] objects) {
08init(context, textViewResourceId, 0, Arrays.asList(objects));
09}
10public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) {
11init(context, resource, textViewResourceId, Arrays.asList(objects));
12}
13public ArrayAdapter(Context context, int textViewResourceId, List objects) {
14init(context, textViewResourceId, 0, objects);
15}
16public ArrayAdapter(Context context, int resource, int textViewResourceId, List objects) {
17init(context, resource, textViewResourceId, objects);
18}
最多有4个参数,分别为当前Context,layout(可省),TextView的ID与数据(可为数组或List),在构造函数中都调用了一个叫init的函数
view sourceprint?1private void init(Context context, int resource, int textViewResourceId, List objects) {
2mContext = context;
3mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
4mResource = mDropDownResource = resource;
5mObjects = objects;
6mFieldId = textViewResourceId;
7}
分别赋值给类中的私有域,mInflater为LayoutInflater,产生相应项的视图。
类中有两个域保存数据
view sourceprint?1private ArrayList mOriginalValues;
2private List mObjects;
其中mOriginalValues用于过滤数据时保存过滤前的数据,将过滤后的数据存入mObjects。
在ArrayAdapter中还定义了add,insert,remove,clear函数用于改变数据,并定义了一个布尔变量mNotifyChange用于表示用这些函数改变数据后是否通知视图(调用notifyDataSetChanged,调用这个函数时会把mNotifyChange置为true。
一些函数的实现:
view sourceprint?01public int getCount() {
02return mObjects.size();
03}
04public T getItem(int position) {
05return mObjects.get(position);
06}
07public int getPosition(T item) {
08return mObjects.indexOf(item);
09}
10public long getItemId(int position) {
11return position;
12}
13public View getView(int position, View convertView, ViewGroup parent) {
14return createViewFromResource(position, convertView, parent, mResource);
15}
16public View getDropDownView(int position, View convertView, ViewGroup parent) {
17return createViewFromResource(position, convertView, parent, mDropDownResource);
18}
view sourceprint?1可以看到getView和getDropDownView都通过调用createViewFromResourse来产生视图。
view sourceprint?01private View createViewFromResource(int position, View convertView, ViewGroup parent,
02int resource) {
03View view;
04TextView text;
05
06if (convertView == null) {
07view = mInflater.inflate(resource, parent, false);
08} else {
09view = convertView;
10}
11
12try {
13if (mFieldId == 0) {
14// If no custom field is assigned, assume the whole resource is a TextView
15text = (TextView) view;
16} else {
17// Otherwise, find the TextView field within the layout
18text = (TextView) view.findViewById(mFieldId);
19}
20} catch (ClassCastException e) {
21Log.e(ArrayAdapter, You must supply a resource ID for a TextView);
22throw new IllegalStateException(
23ArrayAdapter requires the resource ID to be a TextView, e);}
T item = getItem(position);
if (item instanceof CharSequence) {
text.setText((CharSequence)item);
} else {
text.setText(item.toString());
return view;
在createViewFromResource中,首先判断convertView是否存在,若不存在则inflate一个,然后判断mFieldID是否为0,若为0则表示传递给ArrayAdapter的资源ID为一TextView,否则是传递了一Layout,mFieldID为此Layout中TextView的ID。然后通过getItem取得相应位置的数据项,判断是否是CharSequence的实例,如果是直接setText,否则调用其toString()函数,可以ArrayAdapter默认只能给TextVext设置字符串,若要使用其他视图,需要重载getView或getDropDownView,一般情况下会继承BaseAdapter自定义自己的Adapter。
在ArrayAdapter中,还有一静态函数
view sourceprint?1public static ArrayAdapter createFromResource(Context context,
int textArrayResId, int textViewResId) {
CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
return new ArrayAdapter(context, textViewResId, strings);
读取资源文件中定义的字符数组作为数据生成ArrayAdapter,可以看到只能用TextView视图,而不可以指定一Layout再指定Layout中一个TextView的ID。
在ArrayAdapter中还定义了一个ArrayFilter,继承自Filter,用于过滤数据项,其过滤方法为传入一CharSequence,判断相应数据项是否以此CharSequence开头,若不是则用空格分割些数据项,判断分割后的各字符串是否以此CharSequence开头,若是则保留(若数据不是CharSequence则调用其toString())。如果传入的CharSequence为null或长度为0则不过滤。
CursorAdapter
用于显示Cursor中的数据。
在构造函数中可传递一参数autoRequery表示当cursor的数据改变时是否自动调用cursor的requery()以保持视图数据为最新的。
此类中重写了hasStableIds(),返回true。
view sourceprint?1public boolean hasStableIds() {
return true;
view sourceprint?1在CursorAdapter中,重写的getView及getDropDownView判断传入的convertView是否为null,若为null及相应地调用newView()或newDropDownView()来生成一个视图,而newDropDownView()只有一条语句 return newView(context, cursor, parent);所以最后都是调用newView(),newView()为abstract的,需要由子类重写。
view sourceprint?1当通过newView()产生一个View之后,会调用 bindView(v, mContext, mCursor);将cursor中的数据绑定到newView()产生的View之中,此方法同样为abstract的。
view sourceprint?1CursorAdapter实现了接口CursorFilter.CursorFilterClient中的方法
view sourceprint?1//改变cursor指向的数据
public void changeCursor(Cursor cursor)
//将cursor转变为CharSequence,返回或调用cursor.toString()
public CharSequence convertToString(Cursor cursor)
//过滤数据
public Cursor runQueryOnBackgroundThread(CharSequence constraint)
ResourceCursorAdapter
如类名所示,该类继承自CursorAdapter,通过XML产生Views,该类只是简单地重写了一些函数,通过LayoutInflater.inflate将XML转换为View
view sourceprint?1@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(mLayout, parent, false);
@Override
public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(mDropDownLayout, parent, false);
SimpleCursorAdapter
一个ResourceCursorAdapter的简单实现类,用于把cursor中相应的列映射为XML定义的视图中的TextView和ImageView。
该类中定义了一个接口
view sourceprint?1public static interface ViewBinder {
boolean setViewValue(View view, Cursor cursor, int columnIndex);
用于设置把cursor中的列映射为视图的方法。
在SimpleCursorAdapter中重写了bindView,控制cursor到视图的绑定,其定义如下
view sourceprint?01@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewBinder binder = mViewBinder;
final int count = mTo.length;
final int[] from = mFrom;
final int[] to = mTo;
for (int i = 0; i
final View v = view.findViewById(to[i]);
if (v != null) {
boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, cursor, from[i]);
if (!bound) {
String text = cursor.getString(from[i]);
if (text == null) {
text = ;
if (v instanceof TextView) {
setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
setViewImage((ImageView) v, text);
} else {
throw new IllegalStateException(v.getClass().getName() + is not a +
view that can be bounds by this SimpleCursorAdapter);
可以看到,首先检查类中的私有域mViewBinder是否为null(默认为null,可通过setViewBinder)设置,为不为null则通过binder.setViewValue(v, cursor, from[i]); 进行绑定,这个函数若返回true则绑定成功,若返回false则通过SimpleCursorAdapter的规则绑定,判断相应的View是否为TextView或ImageView,若是则绑定,否则抛出异常。
由些可以看到,我们可以自定义一个类实现SimpleCursorAdapter.ViewBinder,然后通过setViewBinder来改变bindView的结果。
SimpleAdapter
一个BaseAdapter的实现类,用于绑定数据到一个XML定义的视图中。数据类型为ArrayList>。
SimpleAdapter也实现了Filter接口用于数据的过滤,过滤方法类似ArrayAdapter,只是其数据类型为Map,要判断Map中的每一项,若任意一顶符合要求就保留。
SimpleAdapter也是通过bindView函数进行数据的绑定,同SimpleCursorAdapter一样,SimpleAdapter也定义了一个相同的内部接口ViewBinder,在bindView中,首先判断是否通过setViewBinder设置了ViewBinder,若设置了则调用其setViewValue进行数据绑定,如果没有设置其setViewValue返回了false,则进行下面的处理:依次判断View是否为Checkable,TextView,ImageView并进行相应的处理,可见默认情况下SimpleAdapter也是处理TextView与ImageView,当然可以setViewBinder。
WrapperListAdapter
继承自ListAdapder的接口,所以也是一个ListAdapter,同时里面嵌入了另一个ListAdapter,只定义了一个函数用于取得嵌入的ListAdapter
view sourceprint?1public ListAdapter getWrappedAdapter();
HeaderViewListAdapter
继承自WrapperListAdapter,当你使用的ListView有页首(Header Views)或页尾(Footer Views)时使用。此类被设计用来作为一个基类,一般不需要使用。
类中定义了两个ArrayList用于保存页首和页尾
view sourceprint?1ArrayList mHeaderViewInfos;
ArrayList mFooterViewInfos;
这两个域不为null,在构造函数中判断,如果给这两个域赋值的参数为null,则将他们赋于值 HeaderViewListAdapter.EMPTY_INFO_LIST,其定义为
view sourceprint?1static final ArrayList EMPTY_INFO_LIST =
new ArrayList();
其中ListView.FixedViewInfo的定义为
view sourceprint?1public class FixedViewInfo {
public View view;ListAdapter#getItem(int)}.
public Object data;
public boolean isSelectable;
该类重定义了getCount,areAllItemsEnabled等函数,把mHeaderViewInfos,mFooterViewInfos同时包括在内。而hasStableIds,getItemViewType与getViewTypeCount只考虑了其内嵌的ListAdapter