RecyclerView

结构

  1. View
  2. Adapter 负责将数据转化为itemview
  3. LayoutManager 负责测量摆放itemview与触摸反馈

RecyclerView.onMeasure() –> LayoutManager.onMeasure()

RecyclerView.onLayout()

​ –> dispatchLayout()

​ –> dispatchLayoutStep2()

​ –>LayoutManager.onLayoutChildren()

对比ListView

Adapter

ListView中复用的是View

RecyclerView复用的是ViewHolder

缓存

ListView两级缓存

RecyclerView四级缓存

动画

ListView动画实现比较麻烦,无法具体得知某个item的改变,也就是无法局部刷新

ItemAnimator

ItemDecoration

绘制分割线或者滚动条之类的元素

运行机制

RecyclerView –> LayoutManager –> Recyler –>Adapter.onCreateViewHolder() 或onBindViewHolder()产生ViewHolder返回Recycler,Recycler再取出View给LayoutManager

ListView复用机制

复用池

View[] mActiveViews = new View[0] 界面上的view没有发生变化时用到,元素会直接复用,不用重新绑数据,比如外部requestLayout,比如早版本sdk在第一次绘制时会触发多次requestLayout

ArrayList<View>[] = mScrapViews 一个ArrayList数组,每一个ArrayList代表一种ViewType在回收池中的一组View,ViewType就是数组索引,所以ListView的ViewType是连续的int值,需要重新绑数据

notifyDataSetChanged()

会把当前界面所有view取出放入mScrapViews,然后取出重新绑定

滑动

滑出的item会把view放入mScrapViews

进入的item会取出,也就是getView中的convertView,如果没有就创建新VIew

RecyclerView的缓存

Recycler

ArrayList mCacheViews

类似mActiveViews,默认最多放两个ViewHolder,作用场景是,向上滑两个item出去,再向下滑两个,此时数据没有变动,可以直接命中mCacheViews中刚缓存的view,直接用,不会走onbindviewholder

mViewCacheMax设置mCacheViews容积

RecyclerViewPool

#RecyclerViewPool
private static final int DEFAULT_MAX_SCRAP = 5 // mScrapHeap中元素最大值,如果itemtype种类少可以设置多点,反之少点

SparseArray<ScrapData> mScrap // 这种设置itemtype就不需要是连续的int值了

class ScrapData {
    ArrayList<ViewHolder> mScrapHeap
}

一直向下滑多于三个item回收,mCacheViews中就会去一个给mScrap,然后加入最新的

向上滑回来,先看mCacheViews,在看mScrap,都不需要重新绑定数据

notifyDataSetChange()进行全局刷新时:

直接刷新所有item,会导致所有item进入回收池,不走mCacheViews直接RecyclerViewPool

notifyItemChanged进行局部刷新时:

mAttachedScrap

ArrayList,没有发生改变的的viewholder暂存区,在一次布局中,所有没有发生改变的的viewholder都会进入这里暂存,然后布局时,layoutmanager从这里取,取完还有剩余则放入pool,如果不够从pool里拿

mChangeScrap

ArrayList,发生改变的的viewholder暂存区

RecyclerView核心机制

pre/post-layout:当RecyclerView发生changed相关调用,比如删除或增加layout,会有一个动画,是通过预测性动画来实现的,changed发生是,RecyclerView会同时进行两个layout布局,一个是改变前,一个是改变后,通过对比来确定动画的起始和终止点。

比较特别的是,当某个item是change,此时这个item需要两个ViewHolder,一个老数据的holder渐隐动画,一个新数据的holder做出场动画。这样可以处理整个item的view都改变的情况。这个机制也导致了即使局部数据改变,change某个item时,依然会有闪烁动画。(可以通过关闭动画避免,或者调用notifyItemChanged(position, “payload”)进行局部刷新,不走预测性动画)

这时候mChangeScrap内存着的就是pre-layout中的layout

当进行pre-layout时,layout从mAttachedScrap和mChangeScrap中获取,而post-layout从pool和cache中获取