今天初七,羊年工作的第一天,這裏f給大家拜個晚年。最近在做項目時候,根據需要涉及到了baseAdapter的一些用法,衆所周知,adapter很好的運用了觀察者模式,f看<head first>時候,看了這個模式,demo也敲過,但是對真正的應用沒有源代碼級別的接觸,於是找時間研究了baseAdapter的源碼,跟大家分享一下。
baseAdapter作爲一個抽象類,實現了listAdapter以及spinnerAdapter接口,它倆又同樣繼承了adapter接口。adapter接口中有一些抽象方法10個
listAdapter新增加了方法2個
spinnerAdapter新增加了方法1個
而baseAdapter實現了
乍一看有些亂,我來捋捋
baseAdapter其實間接,直接的實現了三個adapter裏的方法,listAdapter,spinnerAdapter並未實現adapter的方法,而是新增了方法
registerDataSetObserver(DataSetObserver observer);
unregisterDataSetObserver(DataSetObserver observer);
adapter的這兩個方法是用來註冊 註銷觀察者的。
這兩個方法在baseAdapter裏已經實現
int getCount();
Object getItem(int position);
long getItemId(int position);
View getView(int position, View convertView, ViewGroup parent);
adapter的這四個方法沒有被實現,所以我們在自定義adapter繼承baseAdapter時候,也就要重寫這四個方法
adapter的boolean hasStableIds();在baseAdapter裏實現,返回值是false
這個方法的意思是表達每個item的id對於數據集的改變是否是穩定不變的,如果返回true,表示每個object的id都一樣,
如果都一樣,我們修改時候,在內存中指向的便是同一塊地址,這不合邏輯,所以在baseAdapter裏返回false,即
每個object都有不一樣的id。
int getItemViewType(int position);
int getViewTypeCount();
這兩個方法分別表示通過特定item創建的view的類型和對應的編號
boolean isEmpty();
這個方法表示是否adapter包含數據
listAdapter中兩個抽象方法分別表示,adapter裏的所有item或者指定位置的item是否是enabled,我們遇到相關需求時候,
可以重寫這些方法,實現不同的目的
baseAdapter重寫spinnerAdapter時候,在重寫的方法裏調用了View getView(int position, View convertView, ViewGroup parent);
adapter觀察者模式的體現,就是register這個方法,方法中執行了觀察者對象的註冊
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
BaseAdapter每次新建時候,都會創建一個類似主題的對象
private final DataSetObservable mDataSetObservable = new DataSetObservable();
這裏還會有點亂。。。
DataSetObservable是Observable<DataSetObserver>的實現類,且看registerObserver方法
protected final ArrayList<T> mObservers = new ArrayList<T>();
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
在Android裏 DataSetObservable,DataSetObserver作爲觀察者模式的主題和觀察者對象,已經被綁定在一起
在baseAdapter的重寫的方法中已經把兩個對象作爲參數傳入。
什麼是adapter呢?按文檔解釋,adapter是adapterview和對應的view的數據集之間的橋樑,其實就是爲了顯示數據在listview等顯示而創造的
adapter對於觀察者模式的運用最明顯的是adapter的notifyDataSetChanged方法,
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
看到這以後,f又習慣性地去找onchanged方法,才發現這個方法是個抽象方法,然後就傻了,大家都知道,調用adapter的notifyDataSetChanged
方法後,界面會重新繪製,但是按照目前的邏輯是不會出現畫界面的結果的。。。怎麼解決呢?對對,那就是debug,看代碼是怎麼運行的
在前面其實埋了一個坑,爲何重寫的方法裏有註冊有註銷,但是是怎麼調用的呢?還記得baseAdapter怎麼用嗎?
要新建,然後調用listview 的setAdapter,這個方法,是個及其重要的方法。
這裏對adapter以及觀察者對象做了判斷,剛開始adapter不爲空,觀察者對象爲空,程序不執行註銷的方法,而是往下運行,
下面,執行了觀察者對象的註冊, mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
所以在調用notifyDataSetChanged方法時,是有一個觀察者對象存在。
debug到此,進入方法一看,是執行這一步,
傳遞的Index是0,應該不會報數組下標越界的錯
然後又繼續執行重寫的方法。執行完後回到
再次進入後執行
然後數據集包括與之關聯的view都會被移入堆內存等待循環回收。
好亂的感腳。。。
都是f自己debug很多次得到的結論,adapter塊的源碼確實複雜。
不知道分析是否有錯誤,目前f嚴重處於初級階段,還請大家多多跟f交流,彼此提高技術水平,f也會抽時間找一些有意義的內容和大家共享!