Adapter的繼承結(jié)構(gòu)
Adapter的作用
Adapter是AdapterView視圖與數(shù)據(jù)之間的橋梁,Adapter提供對數(shù)據(jù)的訪問,也負(fù)責(zé)為每一項(xiàng)數(shù)據(jù)產(chǎn)生一個對應(yīng)的View。其作用如下圖所示:

Adapter的繼承結(jié)構(gòu)

各個類的作用
Adapter
Adapter做為這個繼承結(jié)構(gòu)的最頂層的基接口,定義了Adapter要實(shí)現(xiàn)的基本方法:
public interface Adapter {
//注冊一個Observer,當(dāng)Adapter所表示的數(shù)據(jù)改變時會通知它,DataSetObserver是一個抽象類,定義了兩個方法:onChanged與onInvalidated
void registerDataSetObserver(DataSetObserver observer);
//取消注冊一個Observer
void unregisterDataSetObserver(DataSetObserver observer);
//所表示的數(shù)據(jù)的項(xiàng)數(shù)
int getCount();
//返回指定位置的數(shù)據(jù)項(xiàng)
Object getItem(int position);
//返回指定位置的數(shù)據(jù)項(xiàng)的ID
long getItemId(int position);
//表示所有數(shù)據(jù)項(xiàng)的ID是否是穩(wěn)定的,在BaseAdapter中默認(rèn)返回了false,假設(shè)是不穩(wěn)定的,在CursorAdapter中返回了true,Cursor中的_ID是不變的
boolean hasStableIds();
//為每一個數(shù)據(jù)項(xiàng)產(chǎn)生相應(yīng)的視圖
View getView(int position, View convertView, ViewGroup parent);
//為了避免產(chǎn)生大量的View浪費(fèi)內(nèi)存,在Android中,AdapterView中的View是可回收的使用的。比如你有100項(xiàng)數(shù)據(jù)要顯示,而你的屏幕一次只能顯示10條數(shù)據(jù),則
//只產(chǎn)生10個View,當(dāng)往下拖動要顯示第11個View時,會把第1個View的引用傳遞過去,更新里面的數(shù)據(jù)再顯示,也就是說View可重用,只是更新視圖中的數(shù)據(jù)用于顯示新
//的一項(xiàng),如果一個視圖的視圖類型是IGNORE_ITEM_VIEW_TYPE的話,則此視圖不會被重用
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
//獲得相應(yīng)位置的這圖類型
int getItemViewType(int position);
//getView可以返回的View的類型數(shù)量。(在HeaderViewListAdapter中可以包含Header和Footer,getView可以返回Header、Footer及Adapter
//中的視圖,但其getViewTypeCount的實(shí)現(xiàn)只是調(diào)用了內(nèi)部Adapter的的getViewTypeCount,忽略了Header、Footer中的View Type,不懂。
int getViewTypeCount();
static final int NO_SELECTION = Integer.MIN_VALUE;
boolean isEmpty();
}
Adapter有兩個子接口,ListAdapter(列表)與SpinnerAdapter(下拉列表),它們都只定義了少數(shù)方法。一般除WrapperListAdapter接口及其實(shí)現(xiàn)類只實(shí)現(xiàn)了ListAdapter外,都同時實(shí)現(xiàn)了這兩個接口。
ListAdapter
//是否在ListAdapter中的所有項(xiàng)都enabled,即是否所有項(xiàng)都selectable和clickable
public boolean areAllItemsEnabled();
//指定位置的項(xiàng)是否是enabled的
boolean isEnabled(int position);
SpinnerAdapter
//產(chǎn)生相應(yīng)位置下拉項(xiàng)的視圖
public View getDropDownView(int position, View convertView, ViewGroup parent);
BaseAdapter
一個抽象類,Adapter的基礎(chǔ)實(shí)現(xiàn)類,一般作為其他實(shí)現(xiàn)類的基類,同時實(shí)現(xiàn)ListAdapter與SpinnerAdapter,提供了一些方法的默認(rèn)實(shí)現(xiàn):
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//提供一些方法,當(dāng)數(shù)據(jù)改變時調(diào)用注冊的DataSetObserver的回調(diào)函數(shù)
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public boolean hasStableIds() {
return false;
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
//通知相關(guān)聯(lián)的視圖,相應(yīng)的數(shù)據(jù)已經(jīng)改變
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
//通過getView實(shí)現(xiàn)
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
ArrayAdapter<T>
以下是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.
默認(rèn)ArrayAdapter產(chǎn)生的視圖期望一個TextView,或者也可以指定一個Layout并指定其中一個類型為TextView的資源的ID,其底下的數(shù)據(jù)可以是任意類型,但顯示的時候會調(diào)用其toString()方法,也就是說只能用TextView顯示文字,如果想顯示其他的數(shù)據(jù),要重寫getView()
ArrayAdapter有5個構(gòu)造函數(shù)
public ArrayAdapter(Context context, int textViewResourceId) {
init(context, textViewResourceId, 0, new ArrayList<T>());
}
public ArrayAdapter(Context context, int resource, int textViewResourceId) {
init(context, resource, textViewResourceId, new ArrayList<T>());
}
public ArrayAdapter(Context context, int textViewResourceId, T[] objects) {
init(context, textViewResourceId, 0, Arrays.asList(objects));
}
public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) {
init(context, resource, textViewResourceId, Arrays.asList(objects));
}
public ArrayAdapter(Context context, int textViewResourceId, List<T> objects) {
init(context, textViewResourceId, 0, objects);
}
public ArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects) {
init(context, resource, textViewResourceId, objects);
}
最多有4個參數(shù),分別為當(dāng)前Context,layout(可省),TextView的ID與數(shù)據(jù)(可為數(shù)組或List),在構(gòu)造函數(shù)中都調(diào)用了一個叫init的函數(shù)
private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
mContext = context;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResource = mDropDownResource = resource;
mObjects = objects;
mFieldId = textViewResourceId;
}
分別賦值給類中的私有域,mInflater為LayoutInflater,產(chǎn)生相應(yīng)項(xiàng)的視圖。
類中有兩個域保存數(shù)據(jù)
private ArrayList<T> mOriginalValues; private List<T> mObjects;
其中mOriginalValues用于過濾數(shù)據(jù)時保存過濾前的數(shù)據(jù),將過濾后的數(shù)據(jù)存入mObjects。
在ArrayAdapter中還定義了add,insert,remove,clear函數(shù)用于改變數(shù)據(jù),并定義了一個布爾變量mNotifyChange用于表示用這些函數(shù)改變數(shù)據(jù)后是否通知視圖(調(diào)用notifyDataSetChanged,調(diào)用這個函數(shù)時會把mNotifyChange置為true。
一些函數(shù)的實(shí)現(xiàn):
public int getCount() {
return mObjects.size();
}
public T getItem(int position) {
return mObjects.get(position);
}
public int getPosition(T item) {
return mObjects.indexOf(item);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mResource);
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mDropDownResource);
}
可以看到getView和getDropDownView都通過調(diào)用createViewFromResourse來產(chǎn)生視圖。
private View createViewFromResource(int position, View convertView, ViewGroup parent,
int resource) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"ArrayAdapter 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取得相應(yīng)位置的數(shù)據(jù)項(xiàng),判斷是否是CharSequence的實(shí)例,如果是直接setText,否則調(diào)用其toString()函數(shù),可以ArrayAdapter默認(rèn)只能給TextVext設(shè)置字符串,若要使用其他視圖,需要重載getView或getDropDownView,一般情況下會繼承BaseAdapter自定義自己的Adapter。
在ArrayAdapter中,還有一靜態(tài)函數(shù)
public static ArrayAdapter<CharSequence> createFromResource(Context context,
int textArrayResId, int textViewResId) {
CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
return new ArrayAdapter<CharSequence>(context, textViewResId, strings);
}
讀取資源文件中定義的字符數(shù)組作為數(shù)據(jù)生成ArrayAdapter,可以看到只能用TextView視圖,而不可以指定一Layout再指定Layout中一個TextView的ID。
在ArrayAdapter中還定義了一個ArrayFilter,繼承自Filter,用于過濾數(shù)據(jù)項(xiàng)(當(dāng)ListView有焦點(diǎn)時,通過鍵盤輸入字符來進(jìn)行列表項(xiàng)的過濾),其過濾方法為傳入一CharSequence,判斷相應(yīng)數(shù)據(jù)項(xiàng)是否以此CharSequence開頭,若不是則用空格分割些數(shù)據(jù)項(xiàng),判斷分割后的各字符串是否以此CharSequence開頭,若是則保留(若數(shù)據(jù)不是CharSequence則調(diào)用其toString())。如果傳入的CharSequence為null或長度為0則不過濾。
CursorAdapter
用于顯示Cursor中的數(shù)據(jù)。
在構(gòu)造函數(shù)中可傳遞一參數(shù)autoRequery表示當(dāng)cursor的數(shù)據(jù)改變時是否自動調(diào)用cursor的requery()以保持視圖數(shù)據(jù)為最新的。
此類中重寫了hasStableIds(),返回true。
public boolean hasStableIds() {
return true;
}
在CursorAdapter中,重寫的getView及getDropDownView判斷傳入的convertView是否為null,若為null及相應(yīng)地調(diào)用newView()或newDropDownView()來生成一個視圖,而newDropDownView()只有一條語句 return newView(context, cursor, parent);所以最后都是調(diào)用newView(),newView()為abstract的,需要由子類重寫。
當(dāng)通過newView()產(chǎn)生一個View之后,會調(diào)用 bindView(v, mContext, mCursor);將cursor中的數(shù)據(jù)綁定到newView()產(chǎn)生的View之中,此方法同樣為abstract的。
CursorAdapter實(shí)現(xiàn)了接口CursorFilter.CursorFilterClient中的方法
//改變cursor指向的數(shù)據(jù) public void changeCursor(Cursor cursor) //將cursor轉(zhuǎn)變?yōu)镃harSequence,返回""或調(diào)用cursor.toString() public CharSequence convertToString(Cursor cursor) //過濾數(shù)據(jù) public Cursor runQueryOnBackgroundThread(CharSequence constraint)
ResourceCursorAdapter
如類名所示,該類繼承自CursorAdapter,通過XML產(chǎn)生Views,該類只是簡單地重寫了一些函數(shù),通過LayoutInflater.inflate將XML轉(zhuǎn)換為View
@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的簡單實(shí)現(xiàn)類,用于把cursor中相應(yīng)的列映射為XML定義的視圖中的TextView和ImageView。
該類中定義了一個接口
public static interface ViewBinder {
boolean setViewValue(View view, Cursor cursor, int columnIndex);
}
用于設(shè)置把cursor中的列映射為視圖的方法。
在SimpleCursorAdapter中重寫了bindView,控制cursor到視圖的綁定,其定義如下
@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 < count; 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(默認(rèn)為null,可通過setViewBinder)設(shè)置,為不為null則通過binder.setViewValue(v, cursor, from[i]); 進(jìn)行綁定,這個函數(shù)若返回true則綁定成功,若返回false則通過SimpleCursorAdapter的規(guī)則綁定,判斷相應(yīng)的View是否為TextView或ImageView,若是則綁定,否則拋出異常。
由些可以看到,我們可以自定義一個類實(shí)現(xiàn)SimpleCursorAdapter.ViewBinder,然后通過setViewBinder來改變bindView的結(jié)果。
SimpleAdapter
一個BaseAdapter的實(shí)現(xiàn)類,用于綁定數(shù)據(jù)到一個XML定義的視圖中。數(shù)據(jù)類型為ArrayList<Map<String, ?>>。
SimpleAdapter也實(shí)現(xiàn)了Filter接口用于數(shù)據(jù)的過濾,過濾方法類似ArrayAdapter,只是其數(shù)據(jù)類型為Map<String,?>,要判斷Map中的每一項(xiàng),若任意一頂符合要求就保留。
SimpleAdapter也是通過bindView函數(shù)進(jìn)行數(shù)據(jù)的綁定,同SimpleCursorAdapter一樣,SimpleAdapter也定義了一個相同的內(nèi)部接口ViewBinder,在bindView中,首先判斷是否通過setViewBinder設(shè)置了ViewBinder,若設(shè)置了則調(diào)用其setViewValue進(jìn)行數(shù)據(jù)綁定,如果沒有設(shè)置其setViewValue返回了false,則進(jìn)行下面的處理:依次判斷View是否為Checkable,TextView,ImageView并進(jìn)行相應(yīng)的處理,可見默認(rèn)情況下SimpleAdapter也是處理TextView與ImageView,當(dāng)然可以setViewBinder。
WrapperListAdapter
繼承自ListAdapder的接口,所以也是一個ListAdapter,同時里面嵌入了另一個ListAdapter,只定義了一個函數(shù)用于取得嵌入的ListAdapter
public ListAdapter getWrappedAdapter();
HeaderViewListAdapter
繼承自WrapperListAdapter,當(dāng)你使用的ListView有頁首(Header Views)或頁尾(Footer Views)時使用。此類被設(shè)計(jì)用來作為一個基類,一般不需要使用,當(dāng)對一個ListView調(diào)用addHeaderView時會被自動轉(zhuǎn)換為HeaderViewListAdapter,所以用ListAdapter時要注意,其類型可能會改變。
類中定義了兩個ArrayList用于保存頁首和頁尾
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
這兩個域不為null,在構(gòu)造函數(shù)中判斷,如果給這兩個域賦值的參數(shù)為null,則將他們賦于值 HeaderViewListAdapter.EMPTY_INFO_LIST,其定義為
static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
new ArrayList<ListView.FixedViewInfo>();
其中ListView.FixedViewInfo的定義為
public class FixedViewInfo {
public View view;ListAdapter#getItem(int)}.
public Object data;
public boolean isSelectable;
}
該類重定義了getCount,areAllItemsEnabled等函數(shù),把mHeaderViewInfos,mFooterViewInfos同時包括在內(nèi)。而hasStableIds,getItemViewType與getViewTypeCount只考慮了其內(nèi)嵌的ListAdapter

浙公網(wǎng)安備 33010602011771號