標籤:
ListView要想顯示資料,需要用到資料配接器即Adapter。而當我們刪除ListView的某個條目時,資料配接器中的資料來源必然發生改變,這時候我們通過調用適配器類提供的notifyDataSetChanged方法通知listview資料發生改變,請求重新繪製。這其中其實使用了一種比較常見的設計模式,即觀察者模式。
在分析資料適配器中涉及到的觀察者模式之前,我們先簡單瞭解下什麼是觀察者模式。
觀察者模式的定義:定義對象間的一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。
上面是觀察者模式的類圖。Subject類中通過Attach/Detach方法去綁定/解除綁定一個或者多個觀察者(Observer)對象,當Subject出現某種Observer感興趣的事件時,Subject將會調用notify方法通知所有綁定的Observer對象,調用其update方法更新資料。
下面我們試著分析資料適配器中的觀察者模式。這裡我們可以從BaseAdapter的notifyDataSetChanged開始跟蹤源碼。定位到該方法,我們發現只有一行代碼,即調用了mDataSetObservable對象的notifyChanged方法。
public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); }這個mDataSetObservable即被觀察的對象,它是一個DataSetObservable類型。我們來看下其實現:
package android.database;public class DataSetObservable extends Observable<DataSetObserver> { public void notifyChanged() { synchronized(mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } }}可以看到,在它的notifyChanged方法中調用了每一個觀察者的onChanged回調方法,這個mObservers即觀察者集合,它的定義在DataSetObservable的父類Observable中,另外,這裡通過泛型指定了觀察者類型必須為DataSetObserver類型。我們開啟Observable源碼:
package android.database;import java.util.ArrayList;public abstract class Observable<T> { 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); } } public void unregisterObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { int index = mObservers.indexOf(observer); if (index == -1) { throw new IllegalStateException("Observer " + observer + " was not registered."); } mObservers.remove(index); } } ...}這個類定義了一個ArrayList類型的觀察者集合,並且提供了兩個方法用來註冊/解除註冊 一個觀察者,其實就是調用集合的remove/add方法。到這裡我們明白了調用適配器的notifyDataSetChanged方法最終會通知所有已經註冊過的觀察者們,調用每個觀察者的onChanged方法。如果ListView在刪除一個條目後,想要更新介面,必然在此之前註冊了一個觀察者,並且該觀察者的onChanged方法中必然會有介面重繪的代碼。而ListView跟適配器打交道的方式是setAdapter方法,可想而知,此方法中肯定有註冊觀察者的代碼。根據這個思路,我們定位到ListView的setAdapter方法:
public void setAdapter(ListAdapter adapter) { ... ... super.setAdapter(adapter); if (mAdapter != null) { ... ... mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); ... ... } else { ... ... } requestLayout(); }果然,在這個方法中我們找到了註冊觀察者的代碼,但是這裡的觀察者是AdapterDataSetObserver類型的,而Adapter要求的是DataSetObserver類型的,那麼很顯然,AdapterDataSetObserver是DataSetObserver的子類。該類的定義在ListView的父類AbsListView中:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroller != null) { mFastScroller.onSectionsChanged(); } } ... ... }而AbsListView中的AdapterDataSetObserver又繼承了AdapterView類中的AdapterDataSetObserver。最終,我們再AdapterView中找到AdapterDataSetObserver:
class AdapterDataSetObserver extends DataSetObserver { private Parcelable mInstanceState = null; @Override public void onChanged() { mDataChanged = true; mOldItemCount = mItemCount; mItemCount = getAdapter().getCount(); if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null && mOldItemCount == 0 && mItemCount > 0) { AdapterView.this.onRestoreInstanceState(mInstanceState); mInstanceState = null; } else { rememberSyncState(); } checkFocus(); requestLayout(); } ... ... }可以看到,這個類的確繼承了DataSetObserver,並且在onChanged中調用了requestLayout去重新整理布局。到這裡我們明白了整個流程,另外也看到了觀察者模式在實際項目中的使用,確實很強大。最後附上一張圖,方便大家理解。
【安卓筆記】資料配接器(adapter)中的觀察者模式