Android -- Adapter

體系

  • public interface Adapter----0層(表示繼承體系中的層次) 
  • public interface ExpandableListAdapter---(無所謂層次由於沒有其餘接口繼承實現它) 這是adapter的始祖,其餘個性化的adapter均實現它並加入本身的接口。
    • public interface ListAdapter----1層 
    • public interface SpinnerAdapter----1層 
      • public interface WrapperListAdapter----2層(實現ListAdapter)
      • public abstract class BaseAdapter----2層(實現了ListAdapter和SpinnerAdapter) 以抽象類的形式出現構造了類型態下的頂層抽象,包容了List和Spinner 
        • public class ArrayAdapter----3層 
        • public class SimpleAdapter---3層 
        • public class CursorAdapter----3層(CursorAdapter其後還有子類這裏先不探討)

做用

Adapter是AdapterView視圖與數據之間的橋樑,Adapter提供對數據的訪問,也負責爲每一項數據產生一個對應的View。html

Adapter

public interface Adapter { 
    //註冊一個Observer,當Adapter所表示的數據改變時會通知它,DataSetObserver是一個抽象類,定義了兩個方法:onChanged與onInvalidated 
    void registerDataSetObserver(DataSetObserver observer); 
    //取消註冊一個Observer 
    void unregisterDataSetObserver(DataSetObserver observer); 
    //所表示的數據的項數 
    int getCount(); 
    //返回指定位置的數據項 
    Object getItem(int position); 
    //返回指定位置的數據項的ID 
    long getItemId(int position); 
    //表示全部數據項的ID是不是穩定的,在BaseAdapter中默認返回了false,假設是不穩定的,在CursorAdapter中返回了true,Cursor中的_ID是不變的 
    boolean hasStableIds(); 
    //爲每個數據項產生相應的視圖 
    View getView(int position, View convertView, ViewGroup parent); 
    //爲了不產生大量的View浪費內存,在Android中,AdapterView中的View是可回收的使用的。好比你有100項數據要顯示,而你的屏幕一次只能顯示10條數據,則 
    //只產生10個View,當往下拖動要顯示第11個View時,會把第1個View的引用傳遞過去,更新裏面的數據再顯示,也就是說View可重用,只是更新視圖中的數據用於顯示新 
    //的一項,若是一個視圖的視圖類型是IGNORE_ITEM_VIEW_TYPE的話,則此視圖不會被重用 
    static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; 
    //得到相應位置的這圖類型 
    int getItemViewType(int position); 
    //getView能夠返回的View的類型數量。(在HeaderViewListAdapter中能夠包含Header和Footer,getView能夠返回Header、Footer及Adapter 
    //中的視圖,但其getViewTypeCount的實現只是調用了內部Adapter的的getViewTypeCount,忽略了Header、Footer中的View Type,不懂。 
    int getViewTypeCount(); 
    static final int NO_SELECTION = Integer.MIN_VALUE; 
    boolean isEmpty(); 
}

Adapter有兩個子接口,ListAdapter(列表)與SpinnerAdapter(下拉列表),它們都只定義了少數方法。通常除WrapperListAdapter接口及其實現類只實現了ListAdapter外,都同時實現了這兩個接口。java

ListAdapter

//是否在ListAdapter中的全部項都enabled,便是否全部項都selectable和clickable 
public boolean areAllItemsEnabled(); 
//指定位置的項是不是enabled的 
boolean isEnabled(int position);

SpinnerAdapter

//產生相應位置下拉項的視圖 
public View getDropDownView(int position, View convertView, ViewGroup parent);

BaseAdapter

一個抽象類,Adapter的基礎實現類,通常做爲其餘實現類的基類,同時實現ListAdapter與SpinnerAdapter,提供了一些方法的默認實現:數組

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { 
    //提供一些方法,當數據改變時調用註冊的DataSetObserver的回調函數 
    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); 
    } 
    //通知相關聯的視圖,相應的數據已經改變 
    public void notifyDataSetChanged() { 
        mDataSetObservable.notifyChanged(); 
    } 
    public void notifyDataSetInvalidated() { 
        mDataSetObservable.notifyInvalidated(); 
    } 
    public boolean areAllItemsEnabled() { 
        return true; 
    } 
    public boolean isEnabled(int position) { 
        return true; 
    } 
    //經過getView實現 
    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

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; 
   }

類中有兩個域保存數據:app

private ArrayList<T> mOriginalValues; 
private List<T> mObjects;

其中mOriginalValues用於過濾數據時保存過濾前的數據,將過濾後的數據存入mObjects。ide

在ArrayAdapter中還定義了add,insert,remove,clear函數用於改變數據,並定義了一個布爾變量mNotifyChange用於表示用這些函數改變數據後是否通知視圖(調用notifyDataSetChanged,調用這個函數時會把mNotifyChange置爲true。函數

一些函數的實現:ui

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都經過調用createViewFromResourse來產生視圖。this

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取得相應位置的數據項,判斷是不是CharSequence的實例,若是是直接setText,不然調用其toString()函數,能夠ArrayAdapter默認只能給TextVext設置字符串,若要使用其餘視圖,須要重載getView或getDropDownView,通常狀況下會繼承BaseAdapter自定義本身的Adapter。設計

在ArrayAdapter中,還有一靜態函數code

public static ArrayAdapter<CharSequence> createFromResource(Context context, 
            int textArrayResId, int textViewResId) { 
        CharSequence[] strings = context.getResources().getTextArray(textArrayResId); 
        return new ArrayAdapter<CharSequence>(context, textViewResId, strings); 
    }

讀取資源文件中定義的字符數組做爲數據生成ArrayAdapter,能夠看到只能用TextView視圖,而不能夠指定一Layout再指定Layout中一個TextView的ID。

在ArrayAdapter中還定義了一個ArrayFilter,繼承自Filter,用於過濾數據項(當ListView有焦點時,經過鍵盤輸入字符來進行列表項的過濾),其過濾方法爲傳入一CharSequence,判斷相應數據項是否以此CharSequence開頭,若不是則用空格分割些數據項,判斷分割後的各字符串是否以此CharSequence開頭,如果則保留(若數據不是CharSequence則調用其toString())。若是傳入的CharSequence爲null或長度爲0則不過濾。

CursorAdapter

用於顯示Cursor中的數據。

在構造函數中可傳遞一參數autoRequery表示當cursor的數據改變時是否自動調用cursor的requery()以保持視圖數據爲最新的。

此類中重寫了hasStableIds(),返回true。

public boolean hasStableIds() { 
    return true; 
}

在CursorAdapter中,重寫的getView及getDropDownView判斷傳入的convertView是否爲null,若爲null及相應地調用newView()或newDropDownView()來生成一個視圖,而newDropDownView()只有一條語句 return newView(context, cursor, parent);因此最後都是調用newView(),newView()爲abstract的,須要由子類重寫。

當經過newView()產生一個View以後,會調用 bindView(v, mContext, mCursor);將cursor中的數據綁定到newView()產生的View之中,此方法一樣爲abstract的。

CursorAdapter實現了接口CursorFilter.CursorFilterClient中的方法

//改變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。

@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。

該類中定義了一個接口

public static interface ViewBinder { 
    boolean setViewValue(View view, Cursor cursor, int columnIndex); 
}

用於設置把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(默認爲null,可經過setViewBinder)設置,爲不爲null則經過binder.setViewValue(v, cursor, from[i]); 進行綁定,這個函數若返回true則綁定成功,若返回false則經過SimpleCursorAdapter的規則綁定,判斷相應的View是否爲TextView或ImageView,如果則綁定,不然拋出異常。

由些能夠看到,咱們能夠自定義一個類實現SimpleCursorAdapter.ViewBinder,而後經過setViewBinder來改變bindView的結果。

SimpleAdapter

一個BaseAdapter的實現類,用於綁定數據到一個XML定義的視圖中。數據類型爲ArrayList<Map<String, ?>>。

SimpleAdapter也實現了Filter接口用於數據的過濾,過濾方法相似ArrayAdapter,只是其數據類型爲Map<String,?>,要判斷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。

public ListAdapter getWrappedAdapter();

HeaderViewListAdapter

繼承自WrapperListAdapter,當你使用的ListView有頁首(Header Views)或頁尾(Footer Views)時使用。此類被設計用來做爲一個基類,通常不須要使用。

類中定義了兩個ArrayList用於保存頁首和頁尾

ArrayList<ListView.FixedViewInfo> mHeaderViewInfos; 
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;

這兩個域不爲null,在構造函數中判斷,若是給這兩個域賦值的參數爲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等函數,把mHeaderViewInfos,mFooterViewInfos同時包括在內。而hasStableIds,getItemViewType與getViewTypeCount只考慮了其內嵌的ListAdapter.

我是天王蓋地虎的分割線

參考:http://www.cnblogs.com/fww330666557/archive/2012/01/11/2318944.html

相關文章
相關標籤/搜索