多條目控件AdapterView是仿MVC設計模式進行設計的,即AdapterView與資料是分離的,AdapterView并不直接操作資料,而是利用中間件擴充卡Adapter,實際上,完整的AdapterView顯示是三個邏輯部分:AdapterView+Adapter+Data,Adapter相當于MVC中的控制器C,AdapterView相當于MVC中的視圖V,順便說一句,Android程式設計,也是利用MVC控制模式。既然視圖與資料是分離的,那麼當資料有更新時,視圖顯然無法自動更新,Adapter必須實時監控資料變化并重新整理視圖,這裡用到的是Observer(觀察者模式)。
一、什麼是觀察者模式
所謂觀察者模式,聽起來很複雜高大上,實際上響應過程就如同一個點選事件處理過程(實際上也是按照觀察者模式處理,但形式上有所改變),比如一個Button和一個監聽器,Button就是被觀察者(T extends Observable),監聽器就是觀察者(T extends DataSetObservable),當Button狀态發生變化時,被觀察者“賤賤”的向觀察者發出通知,告知其狀态改變,然後觀察者做出相應的處理。觀察者模式比較容易混淆的就是這個“賤賤”,并不是觀察者主動觀察得知被觀察者發生變化,而是被動的等待再作相應的處理。
為實作觀察者模式,Android設計兩個抽象類,分别是DataSetObserver及Observable(可觀察的,即被觀察者),從英文字面上就直覺看出誰是觀察者,誰是被觀察者。下面對此詳細介紹。
二、DataSetObserver(觀察者)抽象類與Observable(被觀察者)抽象類繼承體系
1、DataSetObserver
DataSetObserver類的API這樣描述:DataSetObserver(觀察者)通常設定給Cursor或Adapter,如果資料發生變化則回調該類方法。DataSetObserver類無法單獨調用,必須配合DataSetObservable類使用。該類的執行個體即是Observer,觀察者的重新整理視圖行為是由onChanged以及onInvalidated方法決定的,通常在AdapterView控件,如ListView中繼承該類,并通過重寫這兩個方法,實作針對性的視圖重新整理。
public abstract class DataSetObserver {
//當被觀察的資料發生變化,回調該函數
public void onChanged()
//當被觀察的資料失效,回調該函數
public void onInvalidated()
<span style="font-family: FangSong_GB2312; font-weight: bold; background-color: rgb(255, 255, 255);">2、Observable(被觀察者)抽象類繼承體系</span>
2.1、Observable基類
這是一個抽象基類,該類主要提供對Observer進行注冊或解除注冊到ArrayList資料集(Observer必須綁定到某個資料集才能監測)。 源碼如下:
<pre name="code" class="java">public abstract class Observable<T> {
//觀察者清單,不可重複
protected final ArrayList<T> mObservers = new ArrayList<T>();
//注冊觀察者observer,即将observer添加到mObservers中
public void registerObserver(T observer) {
//解除觀察者observer,即将observer從mObservers中移除
public void unregisterObserver(T observer) {
//解除所有的observer,即清空mObservers清單
public void unregisterAll() {
}
2.2、DataSetObservable類
這是一個可執行個體化類,通常在對ArrayList資料進行更新後,主動調用notifyChanged()函數,若ArrayList資料失效,比如給視圖綁定新的資料源等等,調用notifyInvalidated,源碼如下:
public class DataSetObservable extends Observable<DataSetObserver> {
//當資料集發生變化,回調資料集每一個觀察者DataSetObserver的onChanged函數,通常視圖的重新整理在onChanged中完成
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
//當資料集失效(比如關閉),回調資料集每一個觀察者DataSetObserver的onInvalidated()函數
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
3、小結
DataSetObserver與Observable之間的邏輯關系可以用一張圖表示,很明顯,被觀察通過notifyChanged()與notifyInvalidated()方法通知并調用Observer方法。
三、AdapterView控件與Adapter利用觀察者模式重新整理界面
1、擴充卡Adapter中與DataSetObserver有關的方法
1.1、adapter中相關源碼
//注冊一個observer,觀察Adapter關聯的資料集是否發生變化(如增加、删除等等)
void registerDataSetObserver(DataSetObserver observer);
//取消注冊observer
void unregisterDataSetObserver(DataSetObserver observer);
1.2、BaseAdapter中相關源碼
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//擷取DataSetObservable()對象
private final DataSetObservable mDataSetObservable = new DataSetObservable();
//注冊一個DataSetObserver
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
//取消一個DataSetObserver
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
//通知觀察資料集的observers,資料已經發生變化,并且執行與資料相關的視圖重新整理操作
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
//通知觀察資料集的observers,資料已經失效,一旦調用該函數,表明Adapter也已經失效。
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
1.3、小結
Adapter主要是注冊一個Observer,然後在資料有變化時,主動調用notifyDataSetChanged()或notifyInvalidated(),而決定資料在出現變化後的行為(主要是onchanged()方法決定),一般需要在AdapterView中進行個性定制。
2、AdapterView中與DataSetObserver有關的方法
2.1、AdapterView
AdapterView中寫了一個繼承DataSetObserver的内部類AdapterDataSetObserver,主要是重新onChanged(),onInvalidated()方法,以執行AdapterView的重新整理視圖操作
class AdapterDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
...
}
@Override
public void onInvalidated() {
...
}
}
2.2、ListView
ListView中,主要是setAdapter()方法将擴充卡Adapter綁定到ListView,并設定Adapter的觀察者為AdapterDataSetObserver。
public void setAdapter(ListAdapter adapter) {
mAdapter = adapter;
if (mAdapter != null) {
//注冊Adapter的觀察者AdapterDataSetObserver
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
}
2.3、小結
AdapterView控件主要定義Observer行為,即Onchanged()和onInvalidated()方法,然後通過Adapter的注冊方法registerDataSetObserver()對定制的mDataSetObserver注冊。
3、小結
AdapterView中用一個内部類實作了Observer,Adapter中定義了Observable方法,是以AdapterView是觀察者,Adapter是被觀察者。
AdpaterView與Adapter是通過方法SetAdapter結合起來的,參考ListView的SetAdapter方法(如下),
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
這兩行代碼将AdapterView(觀察者)注冊到Adapter(被觀察者)中,接下來如果Adapter中的資料發生變化,主動調用 adapter.notifyDataSetChanged()即可實作整個觀察者流程。
四、簡單Demo
實作長按長按ListView并删除所在項,主要代碼如下:
listview.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent,
View view, int position, long id) {
//長按删除并動态更新listview
datas.remove(position);
myAdapter.notifyDataSetChanged();
return false;
}
} );