RecyclerView原理分析

RecyclerView


继承关系如下

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {

RecyclerView内部重要的抽象类LayoutManager

LayoutManager的英文注释翻译

在RecyclerView的 layoutManager可以布局和定位ItemVIew,以及使用者看不见的item的回收策略,通过改变layoutManager,RecyclerView可以实现一个标准的垂直滚动的列表,统一网格,交错网格,水平滚动集合和更多。系统提供了几种通用的样式。

LayoutManager的作用

从上面的翻译可以看出,它的作用就是负责RecycleView对ItemView的测量和布局以及回收策略的实现

系统提供几种 LayoutManager通用样式分别为:

(1)LinearLayoutManager(线性布局管理器)

public class LinearLayoutManager extends RecyclerView.LayoutManager implements
        ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider {

(2)GridLayoutManager(表格布局管理器)

public class GridLayoutManager extends LinearLayoutManager {}

(3)StaggeredGridLayoutManager (瀑布流布局管理器)

public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager implements
        RecyclerView.SmoothScroller.ScrollVectorProvider {}

绘制流程

RecyclerView的 onlayout和onMeasure,onDraw内部方法调用

由它的内部抽象类RecyclerView.LayoutManager实现,RecyclerView负责绘制所有decoration;ItemView的绘制由ViewGroup处理,RecyclerView不负责绘制itemVIew,并且是逐个执行的,也就是说执行完一个子控件的onMeasure和onlayout过程,在执行下一个。如下代码:


RecyclerView的onMeasure方法

  @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
             // mLayout测量过程 
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            if (mState.mLayoutStep == State.STEP_START) {                
                //mLayout布局过程
                dispatchLayoutStep2();
            }
           ......代码略
    }

看下方法:dispatchLayoutStep2()

 private void dispatchLayoutStep2() {
   ......代码略
        mLayout.onLayoutChildren(mRecycler, mState);
   ......代码略
    }

进入到onlayoutChildren方法

 @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 
        ...if (mAnchorInfo.mLayoutFromEnd) {
           ...略
            fill(recycler, mLayoutState, state, false);
            // fill towards start
            fill(recycler, mLayoutState, state, false);
            ...略
        } else {
            ...略
            // fill towards end
            fill(recycler, mLayoutState, state, false);
            // fill towards start
            fill(recycler, mLayoutState, state, false);
            ...略

        }
...略
    }

onLayoutChildren确定布局锚点,以此为起点向开始和结束方向填充ItemView,至于填充即使在fill方法里了,这段代码做的工作 如下:
RecyclerView原理分析_第1张图片

接下来进入到fill()方法,看看是如何填充的

 int fill(...) {
        //循环调用layoutChunk
        while (...) {
            layoutChunk(...);
         }   
    }

就是一些系列的判断循环调用layoutchunk方法,进入layoutchunk方法

  void layoutChunk(...) {
                addView(view);

    }

走到这里就是调用ViewGroup的addview方法了,添加View。
以上就是RecyclerView的onMeasure的方法里执行layoutManager的onMeasure和onlayout方法。


RecyclerView的onLayout方法

void dispatchLayout() {
    ...
    if (mState.mLayoutStep == State.STEP_START) {
        dispatchLayoutStep1();
        ...
        dispatchLayoutStep2();
    }
    dispatchLayoutStep3();
    ...
}

发现RecyclerView的layout方法也会执行dispatchLayoutStep()方法,就是也会走layoutManager的onMeasure和onlayout方法。

RecyclerView的layout和measure里都执行了layoutManager的测量和布局方法,那么是执行两边layoutManager的测量和布局方法,还是只执行一次呢?

在RecycleView’中的onMeasure中会判断如果RecycleView支持WRAP_CONTENT,先确定了子控件的大小及位置后,再由此设置RecyclerView的大小;如果是其它情况(测量模式为EXACTLY),子控件的measure及layout过程就会延迟至RecyclerView的layout过程


#####RecyclerView的onDraw方法#####


 @Override
    public void onDraw(Canvas c) {
        super.onDraw(c);

        final int count = mItemDecorations.size();
        for (int i = 0; i < count; i++) {
            mItemDecorations.get(i).onDraw(c, this, mState);
        }
    }

ItemDecoration是啥?
即使保存了ItemView四个方向的偏移量值,可以想象成padding,测量时itemView的itemDecoration保存的对应的偏移量的值也会被计算进去。

在measureChildWithMargins方法中调用获取itemDecoration
的值

public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
        ...
        final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
       ...
            child.measure(widthSpec, heightSpec);
        }
    }

如下就是itemDecoration的偏移量大小+itemVIew自身的大小

RecyclerView原理分析_第2张图片


滑动过程流程分析

分析Recycle的

public boolean onTouchEvent(MotionEvent e) { 
    ...
    switch (action) {
        ...
        case MotionEvent.ACTION_MOVE: {
         //手指滑动距离>滑动阀值 则为SCROLL_STATE_DRAGGING
         //进行滚动
            if (mScrollState == SCROLL_STATE_DRAGGING) {

            scrollByInternal();
        } 
        break;
        ...
        case MotionEvent.ACTION_UP: {
        //手指抬起后 获取之前的滑动距离和时间算出平均速度,由
        //VelocityTracker实现的,然后传入filing进行滚动 
            ...
           此处执行fling()方法 
        } break;
        ...
    }
    ...
}

scrollByInternal()和fling()方法都会调用相应的layoutManager的子类的,比如使用的是LinearLayoutManager,则调用LinearLayoutManager的scrollBy方法

int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    ...
    //此处调用fill方法 进行addview了
    final int consumed = mLayoutState.mScrollingOffset
            + fill(recycler, mLayoutState, state, false);
    ...
    //开始平移View
    mOrientationHelper.offsetChildren(-scrolled);
}

综上述分析就是: Scroller每次计算的滑动偏移量是很小的一部分,而RecyclerView就会根据这个偏移量,确定是平移ItemView,还是除了平移还需要再创建新ItemView
如下图:
RecyclerView原理分析_第3张图片


Recycle(回收机制)


作用:RecycleView的回收机制由它实现
结构:scrapped、cached和exCached集合定义在RecyclerView.Recycler中,
内部集合
(1)scrapped:存放 删除的ItemView
(2)cached:以及缓存和二级缓存 结合默认大小为2
(3)exCached:默认没有实现,需要自己实现
(4)recycled:在RecyclerView.RecycledViewPool中,用于区分Item,用ItemType,本身是个map集合,RecyclerView.RecycledViewPool可以实现在不同的RecyclerView之间共享ItemView,只要为这些不同RecyclerView设置同一个RecyclerView.RecycledViewPool就可以了。
使用的地方:在fill时判断:
首先判断集合cached是否満了,如果已満就从cached集合中移出一个到recycled集合中去,再把新的ItemView添加到cached集合;如果不満就将ItemView直接添加到cached集合。
最后exCached集合是我们自己创建的,所以添加删除元素也要我们自己实现。


数据集、动画


(1)RecycleView对数据集有四种操作:ADD、REMOVE、UPDATE、MOVE,封装在了AdapterHelper.UpdateOp

  static class UpdateOp {

        static final int ADD = 1;

        static final int REMOVE = 1 << 1;

        static final int UPDATE = 1 << 2;

        static final int MOVE = 1 << 3;

        static final int POOL_SIZE = 30;

(2)RecyclerView.RecyclerViewDataObserver.triggerUpdateProcessor()执行时,根据这个等待队列中的信息,对所有子控件重新测量、布局并绘制且执行动画。以上就是我们调用Adapter.notifyItemXXX()系列然后调用RecyclerView.onLayout()会随后调用执行onLayout中的dispatchLayoutStep1()初始化数据,dispatchLayoutStep2(),测量和布局,dispatchLayoutStep3就是添加动画。

你可能感兴趣的:(recyclerview使用,原理)