今天初七,羊年工作的第一天,这里f给大家拜个晚年。最近在做项目时候,根据需要涉及到了baseAdapter的一些用法,众所周知,adapter很好的运用了观察者模式,f看<head first>时候,看了这个模式,demo也敲过,但是对真正的应用没有源代码级别的接触,于是找时间研究了baseAdapter的源码,跟大家分享一下。
baseAdapter作为一个抽象类,实现了listAdapter以及spinnerAdapter接口,它俩又同样继承了adapter接口。adapter接口中有一些抽象方法10个
listAdapter新增加了方法2个
spinnerAdapter新增加了方法1个
而baseAdapter实现了
乍一看有些乱,我来捋捋
baseAdapter其实间接,直接的实现了三个adapter里的方法,listAdapter,spinnerAdapter并未实现adapter的方法,而是新增了方法
registerDataSetObserver(DataSetObserver observer);
unregisterDataSetObserver(DataSetObserver observer);
adapter的这两个方法是用来注册 注销观察者的。
这两个方法在baseAdapter里已经实现
int getCount();
Object getItem(int position);
long getItemId(int position);
View getView(int position, View convertView, ViewGroup parent);
adapter的这四个方法没有被实现,所以我们在自定义adapter继承baseAdapter时候,也就要重写这四个方法
adapter的boolean hasStableIds();在baseAdapter里实现,返回值是false
这个方法的意思是表达每个item的id对于数据集的改变是否是稳定不变的,如果返回true,表示每个object的id都一样,
如果都一样,我们修改时候,在内存中指向的便是同一块地址,这不合逻辑,所以在baseAdapter里返回false,即
每个object都有不一样的id。
int getItemViewType(int position);
int getViewTypeCount();
这两个方法分别表示通过特定item创建的view的类型和对应的编号
boolean isEmpty();
这个方法表示是否adapter包含数据
listAdapter中两个抽象方法分别表示,adapter里的所有item或者指定位置的item是否是enabled,我们遇到相关需求时候,
可以重写这些方法,实现不同的目的
baseAdapter重写spinnerAdapter时候,在重写的方法里调用了View getView(int position, View convertView, ViewGroup parent);
adapter观察者模式的体现,就是register这个方法,方法中执行了观察者对象的注册
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
BaseAdapter每次新建时候,都会创建一个类似主题的对象
private final DataSetObservable mDataSetObservable = new DataSetObservable();
这里还会有点乱。。。
DataSetObservable是Observable<DataSetObserver>的实现类,且看registerObserver方法
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);
}
}
在Android里 DataSetObservable,DataSetObserver作为观察者模式的主题和观察者对象,已经被绑定在一起
在baseAdapter的重写的方法中已经把两个对象作为参数传入。
什么是adapter呢?按文档解释,adapter是adapterview和对应的view的数据集之间的桥梁,其实就是为了显示数据在listview等显示而创造的
adapter对于观察者模式的运用最明显的是adapter的notifyDataSetChanged方法,
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
看到这以后,f又习惯性地去找onchanged方法,才发现这个方法是个抽象方法,然后就傻了,大家都知道,调用adapter的notifyDataSetChanged
方法后,界面会重新绘制,但是按照目前的逻辑是不会出现画界面的结果的。。。怎么解决呢?对对,那就是debug,看代码是怎么运行的
在前面其实埋了一个坑,为何重写的方法里有注册有注销,但是是怎么调用的呢?还记得baseAdapter怎么用吗?
要新建,然后调用listview 的setAdapter,这个方法,是个及其重要的方法。
这里对adapter以及观察者对象做了判断,刚开始adapter不为空,观察者对象为空,程序不执行注销的方法,而是往下运行,
下面,执行了观察者对象的注册, mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
所以在调用notifyDataSetChanged方法时,是有一个观察者对象存在。
debug到此,进入方法一看,是执行这一步,
传递的Index是0,应该不会报数组下标越界的错
然后又继续执行重写的方法。执行完后回到
再次进入后执行
然后数据集包括与之关联的view都会被移入堆内存等待循环回收。
好乱的感脚。。。
都是f自己debug很多次得到的结论,adapter块的源码确实复杂。
不知道分析是否有错误,目前f严重处于初级阶段,还请大家多多跟f交流,彼此提高技术水平,f也会抽时间找一些有意义的内容和大家共享!