前文
在上一篇出现的问题总结起来就是:复用问题,其实布局没那么嵌套复杂,也不会出现,主要是嵌套的GridView导致这么多问题。如果是单纯的ImageView什么的,点击事件或者显示混乱什么的都很好解决。
正文
所以,写个总结来总结下RecyclerView的ViewHolder复用机制吧。先来个表格,如下:
| 回调onCreateViewHolder | 回调onBindViewHolder | 生命周期 | 备注 | |
|---|---|---|---|---|
| mAttachedScrap | 否 | 否 | onLayout函数周期内 | 屏幕内ItemView重用,具体指执行remove的ItemView(匹配postion) |
| mCachedViews | 否 | 否 | 与mAdapter一致,否则存入mRecyclePool | 默认缓存屏幕外2个(匹配postion) |
| mViewCacheExtension | 不直接使用,由用户实现 | |||
| mRecyclerPool | 否 | 是 | 与自身生命周期一致,不被引用时释放 | 默认缓存5个,可以RecyclerView之间共享 |
对应表格的简略版代码
|
|
由表中可以看到RecyclerView其实是四级缓存:
mAttachedScrap用于复用屏幕内的itemView,源码中具体存储的其实是正在执行remove操作的ItemView。(一级缓存)
mCachedViews用于缓存屏幕外的itemView,默认缓存两个,当滑回来的时候,并不会执行onBindViewHolder,更别提onCreateViewHolder了。(二级缓存)
mViewCacheExtension需要我们自己实现,默认是没有的,(三级缓存)
mRecyclerPool四级缓存,默认缓存五个,可以由多个RecyclerView共享,其实mRecyclerPool的内部维护了一个Map,里面以不同的viewType为Key存储了各自对应的ViewHolder集合。可以通过提供的方法来修改内部缓存的Viewholder。可以根据viewType得到对应的itemView,如下代码,
|
|
也可以为 某个特定的viewType设置缓存数量,方法setMaxRecycledViews,即可,不缓存某种就设max为0
|
|
怎么从缓存中读?
依次从这四级缓存中取,如果都没有的话,只能重走onCreateViewHolder和onBindViewHolder了。
怎么写入缓存?
mAttachedScrap用于复用屏幕内的itemView,源码中具体存储的其实是正在执行remove操作的ItemView。这个前文说过。
正常滚动的写入逻辑是:首先判断集合mCachedViews是否满了,
如果已满就从mCachedViews集合中移出一个到mRecyclerPool集合中去,再把新的ItemView添加到mCachedViews集合;
如果不满就将ItemView直接添加到mCachedViews集合。
缓存的是啥
View + ViewHolder(避免每次createView时调用findViewById) + flag(标识状态);
RecyclerView中通过postion获取的是viewholder,即pos ——> (view,viewHolder,flag);标志flag的作用是判断view是否需要重新bindView,这也是RecyclerView实现局部刷新的一个核心.
其他的也不多说了,源码啥的文中也没贴,太长。