【转】从源码来分析ListView
原文:http://yanmingming.sinaapp.com/?p=1251
原文其实不叫这个名字,本文对于原文有一定的修改,觉得这个名字比较适合本篇。
一、ListView 的工作原理
Adapter的作用就是ListView界面与数据交互的桥梁,当列表里面每一项显示到页面上时,都会调用Adapter的getView()方法。
系统需要回执ListView时,首先会调用getCount()函数,得到要绘制的这个列表额长度,然后开始从第一行开始绘制,每行的回执方法是调用getView函数。那么Android是不是为每一行都会新创建一个View呢?试想加入行数为几万行,内存肯定会爆掉的,所以Android官方早就想到了这一点,在ListView实现中添加了视图的缓存-Recycler,每当有行移除屏幕的可视区域时,这个被移除的行的View对象就会被添加到Recycler中,也就是在渲染新行时的那个参数convertView。

二、ListView的初始化

疑问一:ListView绘制时是如何获取每行的View的呢?
首先ListView通过setAdapter方法,将Adapter与ListView关联起来,查看ListView的setAdater方法源码可知,setAdater将传递经来的Adapter的引用复制给了内部全局变量mAdapter,ListView在绘制每行的时候根据行号position调用父类AbsListView中的obtainView,obtainView首先会从recycler中获取是否有匹配的视图,如果存在的话,则调用adapter.getView方法,并传递了scrapView给convertView变量,否则传递的是null。

疑问二:ListView中数据发生变更了,我们一般会调用Adapter的 notifyDataSetChanged()方法, 那么视图是怎么发生变化的呢?
ListView 中的数据适配器Adapter 采用的是观察者模式

ListView在setAdapter时,会新建ApdateDataSetObserver,并注册此观察者。

AdapterDataSetObserver类实在AbsListView中定义:

AdapterView中AdapterDataSetObserver的实现如下(部分省略):

AdapterDataSetObserver 实现了DataSetObserver接口,并重写了onChanger方法,里面调用了requestLayout方法,此方法的作用是要求parent view 重新调用它的哦弄Measure onLayout方法重新布局视图,但不会重新绘制任何视图包括该调用者本身。
requestLayout的实现方法需要到View类中查看:可参考:http://blog.csdn.net/androiddevelop/article/details/8561076

疑问三:RecyclerBin的数据结构是这样的呢?当ListView有多个视图类型(在界面上就是有不同的样式和数据类型)又是怎么选择合适的convertView的呢?
首先看一下RecycleBin的类定义(AbsListView中内部类)

从注释中我们就可以得知 :
RecycleBin一共有两个存储结构分别是ActiveViews 和 ScrapViews
ActiveViews储存当前在界面(手机显示区域)中显示View,移出界面的view会存入ScrapViews
ScrapViews存储当前已经滑动出当前界面(手机显示区域)显示的View,这些view存储起来相当于回收,当再次请求的时候从此存储中取出反复使用。
当ListView中有N个视图类型时,RecycleBin会创建N个scrapView数组,每个类型一个view数组,后面在获取view时会先判断view的类型,然后到对应的数组中去取。

怎么从ScrapViews中获取可用的view视图呢?getScrapView → retrieveFromScrap

retrieveFromScrap(这个不属于RecycleBin类,是属于外部类AbslistView中的方法)
根据position,从mScrapView中找:
1. 如果有view.scrappedFromPosition = position的,直接返回该view;
2. 否则返回mScrapView中最后一个;
3. 如果缓存中没有view,则返回null;
下面,我们来分析下这三种情况在什么条件下满足?
a. 第三种情况,这个最简单:
一开始,listview稳定后,显示N个,此时mScrapView中是没有缓存view的,当我们向上滚动一小段距离(第一个此时仍显示部分),新的view将会显示,此时listview会调用Adapter.getView,但是缓存中没有,因此convertView是null,所以,我们得分配一块内存来创建新的convertView;
b. 第二种情况:
在a中,我们继续向上滚动,直接第一个view完全移出屏幕(假设没有新的item),此时,第一个view就会被detach,并被加入到mScrapView中;然后,我们还继续向上滚动,直接后面又将要显示新的item view时,此时,系统会从mScrapView中找position对应的View,显然,是找不到的,则将从mScrapView中,取最后一个缓存的view传递给convertView;
c. 第一种情况:
紧接着在b中(标示为橙色的文字后面),第一个被完全移出,加入到mScrapView中,且没有新增的item到listview中,此时,缓存中就只有第一个view;然后,我此时向下滑动,则之前的第一个item,将被显示出来,此时,从缓存中查找position对应的view有没有,当然,肯定是找到了,就直接返回了。
【转】从源码来分析ListView的更多相关文章
- 从源码上分析ListView的addHeaderView和setAdapter的调用顺序
ListView想要添加headerview的话,就要通过addHeaderView这个方法,然后想要为ListView设置数据的话,就要调用setAdapter方法了.但是,在调用addHeader ...
- 自定义View系列教程02--onMeasure源码详尽分析
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- MapReduce的ReduceTask任务的运行源码级分析
MapReduce的MapTask任务的运行源码级分析 这篇文章好不容易恢复了...谢天谢地...这篇文章讲了MapTask的执行流程.咱们这一节讲解ReduceTask的执行流程.ReduceTas ...
- Activity源码简要分析总结
Activity源码简要分析总结 摘自参考书籍,只列一下结论: 1. Activity的顶层View是DecorView,而我们在onCreate()方法中通过setContentView()设置的V ...
- MapReduce的MapTask任务的运行源码级分析
TaskTracker任务初始化及启动task源码级分析 这篇文章中分析了任务的启动,每个task都会使用一个进程占用一个JVM来执行,org.apache.hadoop.mapred.Child方法 ...
- TaskTracker任务初始化及启动task源码级分析
在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...
- MongoDB源码分析——mongod程序源码入口分析
Edit 说明:第一次写笔记,之前都是看别人写的,觉得很简单,开始写了之后才发现真的很难,不知道该怎么分析,这篇文章也参考了很多前辈对MongoDB源码的分析,也有一些自己的理解,后续将会继续分析其他 ...
- FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...
随机推荐
- hdu 5443 (2015长春网赛G题 求区间最值)
求区间最值,数据范围也很小,因为只会线段树,所以套了线段树模板=.= Sample Input3110011 151 2 3 4 551 21 32 43 43 531 999999 141 11 2 ...
- Delphi中使用ActiveX的一些心得
使用方法分为两种:一.直接把可视化的ActiveX控件放到程序中:二.运行时根据需要实时建立. 如果是直接使用,则应用程序在初始化的过程中会自动寻找.创建所需的ActiveX控件,如果控件没有注册, ...
- spark web ui中的skipped的含义
顾名思义,跳出的意思啦. 例如如图: skipped的stages代表是已经执行过了.所以不需要再执行了. 如何,你有一个 testRdd.然后先做 testRdd.Filter("xxx& ...
- 028 Partitioner:数据分区器
Partitioner:数据分区器,决定数据到下一个RDD的时候在那一个分区 HashPartitioner:根据key的hashCode值来实现 RangePartitioner: 根据key所属范 ...
- Vue.js学以致用之遇到的那些坑
前段时间的react授权许可的闹剧告诉大家一个问题,只有自己的东西用着才放心.各大巨头也逐渐明白使用自家东西的优势.本来vue的生态就愈加火热,这次的闹剧无疑又加速了vue的发展.当下,国内越来越多的 ...
- 【教程】Tomcat 的catalina.out 日志按照自定义日期格式进行切割
本文简单介绍在使用cronolog对tomcat的日志进行自定义日期格式的切割,方便日志的整理和遇到问题日志的排查! 安装cronolog 安装cronolog的方法网上有很多,这里也简单的介绍一下. ...
- 心跳包(HeartBeat)
http://itindex.net/detail/52922-%E5%BF%83%E8%B7%B3-heartbeat-coderzh 几乎所有的网游服务端都有心跳包(HeartBeat或Ping) ...
- 网页图表Highcharts实践教程标之添加题副标题版权信息
网页图表Highcharts实践教程标之添加题副标题版权信息 Highcharts辅助元素 辅助元素图表的非必要元素,如标题.版权信息.标签.载入动态.它们不和图表数据发生关联,只是额外说明一些基本信 ...
- android onPause OnSavedInstance
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 活动 的 在暂停时候 这个方法 执行结束后,才会执行 下一个活动的 在创建时候 的那个 ...
- 《linux/unix设计思想》读后感
<linux/unix设计思想>这本书,觉得书的大部分内容都闲扯的太远了,以下简单的总结下本书的核心,帮助大家节省时间和金钱. linux/unix设计思想: 1) 程序应该小而专一,程序 ...