RecyclerView源码分析(一)--绘制流程

417 阅读2分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

RecyclerView的使用我们其实都会了,但是它的原理是什么,我们还有不了解的,所以这篇文章主要是对源码进行分析

不管是View还是ViewGroup,必须有三大流程:测量、布局、绘制,所以我们先开看看RecyclerView的绘制流程

一、 onMeasure()

RecyclerView是继承ViewGroup,所以它的本质是自定义view,需要管理绘制流程,我们首先看onMeasure()过程 我们可以将它分为3种情况: (1) 如果当LayoutManager为null的时候,直接使用默认测量方法 (2) 设置了LayoutManager并开启了自动测量功能 (3) 设置了LayoutManager但是没有开启自动测量功能

protected void onMeasure(int widthSpec, int heightSpec) {
    if (mLayout == null) {
        ...
    }
    if (mLayout.isAutoMeasureEnabled()) {
        ...
    } else {
        ...
    }
}
  • 当LayoutManager为null的时候 直接调用defaultOnMeasure(widthSpec, heightSpec)方法 我们看看defaultOnMeasure()方法具体实现
void defaultOnMeasure(int widthSpec, int heightSpec) {
    final int width = LayoutManager.chooseSize(widthSpec,
            getPaddingLeft() + getPaddingRight(),
            ViewCompat.getMinimumWidth(this));
    final int height = LayoutManager.chooseSize(heightSpec,
            getPaddingTop() + getPaddingBottom(),
            ViewCompat.getMinimumHeight(this));
    setMeasuredDimension(width, height);
}
  • 设置了LayoutManager并开启了自动测量功能 (1) 会调用LayoutManager的onMeasure()方法
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

(2) 如果width和height都是确定的值,直接返回

mLastAutoMeasureSkippedDueToExact =
        widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (mLastAutoMeasureSkippedDueToExact || mAdapter == null) {
    return;
}

(3) 如果值不确定,需要根据子View来确定宽高

dispatchLayoutStep2();
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
  • 设置了LayoutManager但是没有开启自动测量功能 (1) 如果RecyclerView已经设置了固定值,直接使用
if (mHasFixedSize) {
    mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
    return;
}

(3) 没有固定值,进行测量

mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

二、 onLayout()

onLayout()中的dispatchLayout()保证RecyclerView必须经历三个过程(dispatchLayoutStep1()、dispatchLayoutStep2()、dispatchLayoutStep3() ),一句话总结就是会根据onMeasure()执行到了哪个,然后会在onLayout()中把剩下的步骤执行

protected void onLayout(boolean changed, int l, int t, int r, int b) {
    TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
    dispatchLayout();
    TraceCompat.endSection();
    mFirstLayoutComplete = true;
}

void dispatchLayout() {
    if (mAdapter == null) {
        return;
    }
    if (mLayout == null) {
        return;
    }
    mState.mIsMeasuring = false;
    ...
    if (mState.mLayoutStep == State.STEP_START) {
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else if (mAdapterHelper.hasUpdates()
            || needsRemeasureDueToExactSkip
            || mLayout.getWidth() != getWidth()
            || mLayout.getHeight() != getHeight()) {
        ...
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else {
        ...
        mLayout.setExactMeasureSpecsFrom(this);
    }
    dispatchLayoutStep3();
}

三、 onDraw()

  • 将Children的绘制分发给ViewGroup;
  • 将分割线的绘制分发给ItemDecoration
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);
    }
}

绘制流程大概就完了,下一篇分析Recycler的缓存机制