上篇实现自定义一个LinearLayoutManager(http://www.jianshu.com/p/e8b5e43ace31)时
后发现一个神奇的现象
我们看下这张gif,可以看到我们一开始屏幕创建并显示了12条视图,当我们开始滑动时,第一个ViewHolder被回收后,并没有被第13个复用,并且同样当第二个ViewHolder被回收后同样没有被第14个复用,直到第16个ViewHolder被创建时才复用了第一个被回收的ViewHolder,咋这么奇怪呢?按照我们理解不应该第一个被回收后紧接着被刚出现的第13个复用吗?
我们根据源码来分析一下,在前面一篇带你自定义一个LayoutManager文章中:
http://www.jianshu.com/p/e8b5e43ace31 我们移除并回收视图时会去调用这个方法
可以看到这个方法中主要做了2件事情,将这个child view从recyclerview中移除,然后调用Recycler这个对象的recyclerView方法去回收这个child view,Recycler这个对象主要提供的功能顾名思义就是view视图的回收以及复用,接着看看回收view都做了哪些事情。
这里主要先判断这个ViewHolder中的view是否已经detached解除依附状态,如果没有的话会解除依附状态并从父布局中remove这个view,解除依附状态的目的是因为Recycler有多级缓存,attach状态的view会存放在mAttachedScrap这个list集合中,这个缓存中的view表示是不需要重新绑定数据的,所以当我们需要回收这个view时是需要从scap集合中移除这个ViewHolder并改变它的状态。
我们主要关注recycleViewHolderInternal这个方法,代码比较长而主要的逻辑就在这一段
当mCachedViews集合长度等于mViewCacheMax时,才调用recycleCachedViewAt(0)这个方法,我们看下原来mViewCacheMax这个变量设定了一个默认值
这个值被系统默认设置为2,recycleCachedViewAt(0)这个方法点进去看看
这段代码会把mCachedViews第0位置的holder取出并调用addViewHolderToRecycledViewPool这个方法然后从mCachewdViews集合中移除这个position,
接着跟进addViewHolderToRecycledViewPool这个方法
最后一行会调用getRecycledViewPool()方法获取到RecycledViewPool然后调用putRecycledView方法,把mCacheViews index0位置的viewHolder取出然后存入进去
具体怎么存入进去的呢?关键位置在于这一段代码,我们看看具体实现
final ArrayList scrapHeap = getScrapHeapForType(viewType);
这里从mScrap中根据这个view对应的viewType取一个集合,如果取出来为null,说明之前没有为这个viewType存过那么新创建一个集合然后以这个viewType作为key存入mScrap,创建的这个集合才是真正回收我们每种type对应viewHolder的容器,后面复用的holder都会从这个容器里面去取!
下面还有个mMaxScrap集合,那么它是干什么用的呢?简单说它存储的类型就是
Key:不同的viewType value:每种viewType能被回收的最大数量
官方这个默认值为5,这个值我们是可以自己设置去更改的
前面putRecyclerView方法中我们可以看到如果我们回收的集合长度大于等于这个默认值,那么直接return不走下面回收逻辑了。
回收这段逻辑分析完了接着分析获取view的时候,从getViewForPosition分析,代码也是比较长重点在这一段
获取到RecycledViewPool然后调用getRecycledViewPool方法获取holder
重点来了,这里从前面分析的mScrap中拿到集合然后拿到type对应的holder集合后,从这个集合中移除这个holder,然后返回这个holder.
全程分析源码比较繁琐也比较长,总结一下:
整体回收逻辑就是mCachedViews只是一个获取复用Holder的缓存集合,存储我们回收的每一个view,而我们新的Holder并不是回收后就立马获取,这个缓存集合有个阈值,默认是2,当缓存集合的长度小于2时,就一直往集合里存储回收的Holder,当缓存长度大小等于2时,就创建一个ArrayList集合存储mCachedViews集合第0位置的Holder并在mCachedViews中移除这个Holder,最后把这个集合存入mScrap对应type的集合中. 我们获取view的时候就会最终从mScrap中取出这个集合并拿到复用的holder,并在ArrayList集合中移除Holder后返回这个Holde,这样就保证mScrap集合中存的是唯一等待复用的那个Holder,而mCachedViews中存的始终是2个最新等待回收的Holder。