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去刷新布局。到这里我们明白了整个流程,另外也
看到了观察者模式在实际项目中的使用,确实很强大。
最后附上一张图,方便大家理解。