我們每次在使用ListView的時(shí)候,如果數(shù)據(jù)改變了,都會(huì)調(diào)用adapter的notifyDataSetChanged方法,那么我們來看看為什么調(diào)用該方法就可以刷新數(shù)據(jù)了。
首選我們要了解訂閱模式,因?yàn)閚otifyDataSetChanged就是使用該模式。先看下UML建模圖吧

我們先介紹下各個(gè)類:
- Observer:抽象觀察者
- ConcreteObserverA:觀察者對(duì)象實(shí)體
- Observable:抽象被觀察者
- ConcreteObservers:被觀察者實(shí)體
這是一個(gè)最簡(jiǎn)單的觀察者模式,目標(biāo)對(duì)象能夠添加和刪除觀察者,當(dāng)自己某種狀態(tài)或者行為發(fā)生改變時(shí),可通過notifyObservers通知注冊(cè)的觀察者進(jìn)行更新操作。
了解了訂閱模式,那我們看看ListView與Adapter是怎么實(shí)現(xiàn)的。先從我們最常調(diào)用的notifyDataSetChanged方法切入吧
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
}
BaseAdapter中有一個(gè)成員變量mDataSetObservable,我們的注冊(cè),注銷都是調(diào)用它的方法,那么我們看看它是什么
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
}
這里只有一個(gè)簡(jiǎn)單的方法notifyChanged,重要的東西應(yīng)該在它的父類。DataSetObservable是繼承Observable的,那我們看看Observable
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);
}
}
}
這時(shí)候我們就能看到被觀察者的影子了,有一個(gè)集合,來存儲(chǔ)觀察者的引用,等到數(shù)據(jù)變化時(shí)再遍歷通知,只是沒有notify方法,因?yàn)樗淖宇愄峁┝嗽摲椒ā?br>
所以DataSetObservable就是我們的被觀察者,它可以添加、刪除、通知觀察者。被觀察者我們找到了,那么觀察者有是誰呢?剛才有沒有注意到BaseAdapter
中有兩個(gè)方法,將一個(gè)DataSetObserver從我們的被觀察者DataSetObservable中注冊(cè)、注銷。那么DataSetObservable肯定是我們要找的
觀察者了,看看誰調(diào)用了這兩個(gè)方法;我們看看listView的SetAdapter,這里調(diào)用了adapter的注冊(cè)與注銷方法(把觀察者綁定到被觀察者上)
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);//注銷
}
...
if (mAdapter != null) {
...
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);//重新注冊(cè)
...
} else {
...
}
requestLayout();
}
ListView先將之前注冊(cè)的DataSetObserver注銷掉,然后再new一個(gè)新的AdapterDataSetObserver注冊(cè)到我們的被觀察者上
這樣我們就找到了觀察者AdapterDataSetObserver,其實(shí)AdapterDataSetObserver是AbsListView中的內(nèi)部類
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
}
從AdapterDataSetObserver中我們沒有找到更新UI的跡象,那么我們看看它的父類
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();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
mInstanceState = AdapterView.this.onSaveInstanceState();
}
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
這里我們看到在listview中調(diào)用了requestLayout(),應(yīng)該就是這里更新的。先保留懸念,我們繼續(xù)理清類的關(guān)系
AdapterDataSetObserver最終是繼承自DataSetObserver
public abstract class DataSetObserver {
public void onChanged() {
// Do nothing
}
public void onInvalidated() {
// Do nothing
}
}
DataSetObserver跟我們UML圖中的Observer很像?到這里我們就找到了訂閱模式中所有的元素,讓我來列舉下
- DataSetObserver:抽象觀察者
- AdapterDataSetObserver:觀察者實(shí)體
- Observable:抽象被觀察者
- DataSetObservable:被觀察者實(shí)體
既然找到了訂閱模式中這些元素,那么我們分析下adapter與listView是如何使用訂閱模式的;
當(dāng)adapter中的數(shù)據(jù)發(fā)生改變時(shí),adapter會(huì)調(diào)用notifyDataSetChanged方法,這個(gè)方法會(huì)調(diào)用被觀察者DataSetObservable
的notifyChanged方法,這個(gè)方法又會(huì)遍歷調(diào)用所有觀察者AdapterDataSetObserver的onChanged方法。在AdapterDataSetObserver
父類onChanged方法中會(huì)重新計(jì)算adapter的數(shù)據(jù)個(gè)數(shù),并調(diào)用ListView的requestLayout方法重新布局,更新用戶界面。
至此就解釋了為什么adapter.notifyDataSetChanged方法會(huì)更新界面
ListView訂閱模式UML(關(guān)系指向不準(zhǔn),僅供參考)
