视图资源回收(Recycler)
·基本文档解释
负责管理废弃的或者已经分离的item视图,使视图重用。
“废弃的”的定义:仍然附着在RecyclerView上,但是被标记用于移除或者复用。
由LayoutManager使用Recycler的典型用途是,获得适配器数据集的视图,该视图用于表示给定位置和item的ID的数据。
如果被复用的视图,被认为是“dirty”,将会被适配器重新绑定一次,如果不是“dirty”的,直接被复用,不会重新测量,除非调用isLayoutRequested()。
·看源码
对于一个没有看过源码的人,其实看源码挺难的,难在心静不下来,难在英文文档难翻译,最难的是你的坏习惯:遇事欠思考,不絮叨了,总之方法很重要。
源码不贴了,进来就列了4个List<ViewHolder>集合,mAttachedScrap、mChangedScrap、mCachedViews、mUnmodifiableAttachedScrap。字面上理解一波:
mAttachedScrap | 未脱离的,废弃的ViewHolder |
mChangedScrap | 脱离的,废弃的ViewHolder |
mCachedViews | 缓存的ViewHolder |
mUnmodifiableAttachedScrap | 不可修改的,未脱离的,废弃的ViewHolder |
显然是四个管理ViewHolder的集合,Viewholder是什么?它是用来描述item视图和元数据的。猜测一波,这四个集合里,如果ViewHolder不是空,Adapter就不会去创建新的,而是直接复用。接下来,源码验证:
先找到返回值是ViewHolder的方法(Ctrl+O),getChangedScrapViewForPosition、getScrapOrCachedViewForId、getScrapOrHiddenOrCachedHolderForPosition、tryGetViewHolderForPositionByDeadline;
然后发现,前三个方法,在tryGetViewHolderForPositionByDeadline方法里调用了,那么关键逻辑一定在这里。
文档解释:试图得到给定位置的ViewHolder,要么从废弃站、缓存、回收池获取,要么就直接创建。到此为止,证实了前面的猜测。代码暂且不深入看了,此文只做一个概述。
通过上述,得知Recycler采用了多级回收机制。就四个集合处理了回收?继续看源码,发现了RecycledViewPool,ViewCachExtension,cach和recycler这俩单词,可以推测,肯定与回收有关系。
进入RecyclerViewPool(回收视图池),Ctrl+O,看一下内部结构,果然发现了关于ViewHolder的方法getRecycledView,先不管方法内部实现,看到这里就OK了,和上面的猜测一致,继上面四个集合之后,RecycledViewPool是第五个用来管理回收Viewholder的。
进入ViewCachExtension(视图缓存扩展),文档解释:这是一个帮助类,可以帮助开发者控制缓存规则。注意这句:当getViewForPosition(int)被调用时。回收器先检查未脱离的废弃站和一级缓存,寻找一个匹配的视图,如果没找到合适的,回收器在检查回收池之前,会调用getViewForPositionAndType(Recycler,int,int)方法。
这样讲更好理解:先从废弃站和一级缓存里找,再从视图缓存扩展里找,再从回收池里找,都没找到ViewHolder,就创建一个。那么,这是第六个管理ViewHolder的。
getViewForPosition(int)初始化视图;getViewForPositionAndType(Recycler,int,int)返回一个可以绑定的视图,此视图不是新建的,而是可以重用的。
·总结Recycler
为了更清晰,两句话总结:
①Recycler是回收器,有着多级回收机制,由上面分析可知,有6个家伙在管理着ViewHolder
②Recycler管理废弃或者脱离的视图,让视图复用。
项目中预见到问题
给item添加事件,改变item中的箭头方向,加载更多时,就会出现箭头方向不一致,自己脑补一下。
原因:属性动画改变了箭头方向,被RecyclerView缓存了,这个ViewHolder被复用了。
解决:方法①属性动画改成传统动画,原理:传统动画并没有对箭头进行实质的改变,所以缓存的是原本的视图。
方法②onViewDetachedFromWindow(VH holder)里面,处理一下箭头方向,归位。原理:这个方法是当item脱离出窗口之后的操作,也就是缓存视图时的处理。