之前写程序就一直没搞懂Adapter的notifyDataSetChange()方法为什么会根据数据源来刷新ListView的列表,在网上荡了很久却总不得精髓,今天因为要用耐着性子很仔细的研究了一下。(斜体的看不懂可以略过)
首先说一下观察者模式,这个网上介绍很多,但是说实话很简单的一个东西我不明白他们干嘛要讲那么多,我给大家简单的来说一说(是不是侮辱了艺术)。首先我们定义两个类,一个观察者类A,一个被观察类B,那被观察类B中有一个观察者A的引用,然后呢,当被观察者B调用更新数据的方法的时候就会顺带执行观察者A的某一个既定的方法。简单点说就这个意思。其实我记得我之前在看观察者模式的时候是结合这ListView来的,可是说实话,ListView中的观察者模式究竟是谁观察谁呢,因为当时写的项目的云因,当时我看的可真是云里雾里。先把这个观察者模式说完啊。那一般在各种语言环境中,语言的开发者都会给你写好一些设计模式的固定接口,方便你的使用,那在java中的观察者模式也是一样的。
一般呢会给你两个接口一个是观察者的接口叫Observer吧,另一个是被观察者的接口就叫Observable。然后呢里面会分别会定义不同的方法和变量。那需要什么方法和变量呢?我把代码写上来
//**观察者抽象类**
public abstract class Observer{
void onChange();//
}
//**被观察者抽象类**
public abstract class Observable{
//定义一个List集合,用来存放Observer的引用,因为Observer是可以有很多个的
private final List<Observer> mObservers= new ArrayList<Observer>();
void registerDataSetObserver(Observer observer){
//给list集合中添加一个观察者
mObservers.add(observer);
}
void unRegisterDataSetObserver(Observer observer) {
//将一个观察者从list集合中移除
mObservers.remove(observer);
}
public void notifyChanged() {
//这个遍历观察者的方法我是参考着android源码来写的,发现没,这里用i--来遍历观察者,
//那谷歌给的注释是这么说的:
// since onChanged() is implemented by the app, it could do anything,
// includingremoving itself from {@link mObservers} - and that could cause
// problems ifan iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
//这个是什么意思呢,就是说,自从谷歌把onChange()方法开放给app以后,
//在onChange()方法中就可以执行任何操作,包括从mObservers中移除它自己。那如果倒着来随便你
//怎么移除你自己,谁管你,又不会影响到其他的观察者,真是面面俱到啊,虽然我还没用过这种情况
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
//为了尽量简化代码,增加可读性便于理解,关于多线程操作和该观察者是否已存在于list集合中
}
观察者模式讲完了,下面开始来分析ListView是怎么将观察者模式应用起来的。
首先我们先来说说这张图的上半部分,把BaseAdapter中的源码给大家贴上来啊
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public boolean hasStableIds() {
return false;
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
可以看到里面有一个mDataSetObservable参数,那这个参数是来干嘛的呢,可以看到他的类型是DataSetObservable,由上图又可以看到DataSetObservable实现了抽象类Observable,并且呢定义了刷新的方法notifyChanged,注意到没有,谷歌的这个监听者模式把注册监听者和遍历监听者这两个方法放在了两个类中,并且BaseAdapter并不是直接继承DataSetObservable来作为一个被监听对象,而是在自己的空间中定义了一个DataSetObservable对象,来实现给监听者传递消息,何必呢你说。难道就是为了改个名字。。。。。构造的这么复杂,还真是这样,就是为了给遍历监听者的方法改个名字,毕竟在外面包一层就可以改名字了嘛。嘿嘿,说详细点吧,其实吧改个名字确实不假,它更深一层的意思是什么呢,以这种方式你可以绑定不同的监听者来监听不同的对象,并且可以通过该方法名字的方式做到很好的代码可读性。而且呢,谷歌更厉害的是在实现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);
}
}
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
看到没这是一个泛型类,那这个泛型类起什么作用呢?谷歌是这么用的,你看代码public class DataSetObservable extends Observable<DataSetObserver>{}
利用它就可以实现一种类型的观察者和另外一种类型的被观察者一一照应,就比如DataSetObservable这个被观察者就只能被DataSetObserver所观察,只能执行DataSetObserver中的onChange方法,如果有一个XXXObserver是不能被注册到DataSetObservable,只能重新定义另外一个XXXObservable extends Observable。知道了吧。
咱们再说图片中下面的那部分,可以看到在setAdapter( )方法中,执行了adapter中的registerDateSetObserver(mDataSetObserver)方法,那这个mDataSetObserver是个什么?你会发现在ListView里面似乎根本就没有这个对象(注意似乎没有,实际上是有的),其实他是在ListView的父类AbsListView里面,AbsListView里面有这样一句代码AdapterDataSetObserver mDataSetObserver;
那AdapterDataSetObserver又是什么类型呢?他是AbsListView里面的一个内部类,看看它的代码啊
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
}
可以看到 AdapterDataSetObserver是继承了AdapterView的内部类AdapterDataSetObserver,这两个内部类名字是一样的,那AdapterView又是什么呢,AdapterView是AbsListView的父类,AdapterView呢是一个抽象类,这个抽象类就是可以和Adapter配合的最祖宗级别的类了,我先把AbsListView中 那个观察者贴过来啊,因为在AbsListView中的mDataSetObserver所对应的类是mDataSetObserver.AdapterDataSetObserver,代码如下:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {//这个是和ListView相关的右边那个快速导航的东西,你可以自己百度一下
mFastScroll.onSectionsChanged();
}
}
}
可以看到在onChange的第一行代码中,直接就执行了一句supe.onChange( )方法,也就是说同时调用了AdapterView这个祖宗级别的类里面的AdapterDataSetObserver中的onChange( )方法,再看看里面都执行了什么,
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
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()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
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;
}
}
可以看到里面有一句requestLayout(),那么这句代码就是用来刷新ListView的,那这个方法究竟是怎么来刷新ListView的呢????抱歉,我看了一下,没看懂,回头有时间研究一下ListView的绘制流程再来给大家作分析。
关于ListView的观察者模式就算介绍完了,总结一下,Android的观察者模式是这样一种类型,定义一个抽象观察者类A、一个抽象的别被观察者类B并且采用泛型的形式,然后呢,写一个A的实现AA extends A,一个B的实现类BB extends B<AA>{public void notify(){aa.onChange};}//aa是AA类的对象
然后呢,再在真正的被观察者X中定义一个BB变量,真正的观察者中定义一个AA变量,并实现相应的方法,从而达到屌的不行的设计方式。