ListView中的观察者模式
来源:互联网 发布:xshell mac 代替 编辑:程序博客网 时间:2024/05/23 12:01
【原文地址 点击打开链接】
虽然现在RecyclerView 很好用,也在逐渐替代 ListView。很多github的开源大神也在对其进行更加实用的封装。我现在写的一个音乐播放器也在使用 RecyclerView。但是这些都不阻碍我们学习 ListView 优秀的源码设计。
进入正题,我用的是 Api-23 的源码。接下来就从源码的角度带你学习ListView 中的观察者模式。
当我们开启异步线程,向服务端拉取数据后,数据源已经更新了,此时想要更新ListView 的视图以显示新的数据。
ListView使用了Adapter模式,很简单只需一行代码就能完成 ListView 的更新。
mAdapter.notifyDataSetChanged();
那么这里引出一个问题:
更新ListView 的工作,是 Adapter完成的 还是 ListView自身内部完成的?可以先猜想一下再往下看。
因为我之前已经学习过自定义控件,所以我看源码之前猜想是 ListView 完成的。惯性使然,我想到他可能是调用了 onLayout(),onDraw() 等方法呀,去重新布局,绘制。
那接下来就解开疑惑吧。
先找到源头,从ListView绑定Adapter 那里开始:
mListView.setAdapter(mAdapter);
ListView和Adapter 就是用这行代码建立起关联的。
那么跟踪 setAdapter方法进去:
方法是这样开始的:
if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver);}
先判断mAdapter != null && mDataSetObserver != null
mAdapter 肯定是 不为null 的,那么mDataSetObserver 呢?这个引用是哪里被赋值的,先不管,继续往下看 setAdapter 方法。
这里先分享我看源码的方法吧:
刚开始的时候我是很喜欢往深处闯,导致看了一天都无法自拔,思路又散了。
现在我看源码都是挑重点看,比如这个setAdapter 方法,一路看下来都没有 return 语句跳出,那么就一定会来到 if(mAdapter !=null )这个判断,如下:
到了这里,我们也就找到了mDataSetObserver,原来是在这里被赋值的。现在得出小结论:
在 ListView 的 setAdapter方法 中,生成了一个AdapterDataSetObserver 对象并赋值给 mDataSetObserver。
调用 Adapter 的registerDataSetObserver 方法将 mDataSetObserver 注册进去。
现在我们好奇的是 Adapter 的registerDataSetObserver 方法。继续前进。
在BaseAdapter 类中找到了 registerDataSetObserver 方法,并且也找到了经常调用的,很熟悉的 notifyDataSetChanged 方法。如下:
可以看到,在registerDataSetObserver 方法中,又调用了 DataSetObservable 的 registerObserver 方法将传进来的 AdapterDataSetObserver 对象注册进去,那么这个 DataSetObservable 又是什么呢?继续跟进。
这个DataSetObservable 源码比较少,那就全部贴出:
好像看不太懂。mObservers是什么?竟然没有 registerObserver 方法。哈哈,那肯定是父类继承下来的啊。在 DataSetObservable 类中暂时没我们想要知道的信息,那么就看看他的父类 Observable 吧。Observable还是个泛型。不管,看内部实现原理就好:
找到了registerObserver 方法。代码逻辑还挺简单的。我们又可以得出小结论:
DataSetObservable的内部维护着一个观察者集合,即源码中的mObservers。当我们的 ListView 绑定了 Adapter,调用 BaseAdapter 的 registerDataSetObserver 方法时,实际上是在这个观察者集合 mObservers 里将该观察者添加进来。对 ListView 来说,这个观察者就是 AdapterDataSetObserver。
完成注册。以上就是setAdapter 方法的源码分析。
再看到BaseAdapter的notifyDataSetChanged() 方法:
public void notifyDataSetChanged() { mDataSetObservable.notifyChanged();}
内部调用了DataSetObservable 的 notifyChanged 方法。
再回到DataSetObservable 的源码,看到 notifyChanged() 方法:
从观察者集合里遍历出观察者,并调用该观察者的onChange() 方法,很清楚了吧。
当我们调用Adapter 的 notifyDataSetChanged 方法 更新ListView。
在notifyDataSetChanged 方法中又会调用 DataSetObservable 的 notifyChanged 方法。
而从DataSetObservable 的源码中,我们知道了在 notifyChanged 方法中又会遍历出 AdapterDataSetObserver(观察者),并调用这个观察者的 onChanged() 方法。
完毕,底层实现就是这样。
接下来只需要知道AdapterDataSetObserver(观察者)的 onChanged() 方法里做了什么就好了。
而AdapterDataSetObserver,是 ListView 的父类 AdapterView 的一个内部类。他是真的有 onChanged 方法的。不信你看:
终于揭开谜底,在AdapterDataSetObserver 的 onChanged()方法里,实际上是调用了 View 的 requestLayout() 方法进行重新策略,布局,绘制整个 ListView 的 子项item view。
requestLayout() 的源码如下:
AdapterView是继承ViewGroup 的,但是 ViewGroup 并没有重写 requestLayout()方法。有能力的同学可以继续深入研究 AdapterView 到底是怎么重新布局的。
至此,我们已经解开了开篇的疑惑。
综上所述,AdapterDataSetObserver这个是观察者,在 AdapterDataSetObserver 的 onChanged 函数中,实际上调用的是 View 中的方法完成了整个 更新ListView 的工作,AdapterDataSetObserver只是在外层进行了包装,真正的核心功能是 ListView,更加准确的说话是 ListView 的父类 AdapterView。
ListView就是通过Adapter模式,观察者模式,子项复用机制实现了视图良好的扩展性,节约了内存开销,提高了运行效率。
- ListView中的观察者模式
- ListView中的观察者模式
- ListView中的观察者模式
- ListView中的观察者模式
- ListView BaseAdapter中的观察者模式
- (转)ListView BaseAdapter中的观察者模式
- 设计模式之观察者模式在Listview中的应用
- (转)设计模式之观察者模式在ListView中的应用
- 最熟悉的陌生人:ListView 中的观察者模式
- 最熟悉的陌生人:ListView 中的观察者模式
- 第十四篇: 最熟悉的陌生人:ListView 中的观察者模式
- Android ListView观察者模式浅析
- ListView观察者模式的应用
- YUI中的观察者模式
- symbian 中的观察者模式
- Java 中的观察者模式
- java中的观察者模式
- C#中的观察者模式
- mysql 获取当前日期周一和周日
- js中checkbox中的全选和反选效果实现
- 第八周-项目四 字符串加密
- poj 1155(树型dp)
- 【GOLANG】第一章 RPC 源码分析
- ListView中的观察者模式
- 第八周项目1-建立顺序串的算法库
- 文件下载,上传,压缩,解压
- python list列表删除元素需要注意的问题
- 2440时钟设置浅谈(带程序)
- mybatis三表连接查询数据重复问题
- 模逆元,扩展欧几里德算法详解
- iOS解决duplicate symbol报错
- 第八周项目四--字符串加密