RecyclerView源码学习笔记(三)RecycleView的绘制过程onMeasure,onLayout,onDraw

前言前两篇讲了RecyclerView的构造函数和setLayoutManager以及setAdapter方法,这篇就开始学习RecyclerView的真正显示过程RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法RecyclerView源码学习笔记(二)setAdapter内容RecyclerView继承自ViewGroup,所以也...
摘要由CSDN通过智能技术生成

前言

前两篇讲了RecyclerView的构造函数和setLayoutManager以及setAdapter方法,这篇就开始学习RecyclerView的真正显示过程

内容

RecyclerView继承自ViewGroup,所以也遵守一般view的绘制过程,首先会调用onMeasure方法,我们就先看onMeasure方法

onMeasure

onMeasure的作用是决定自身和子view的尺寸,这点要事先清楚。

好,直接上源码,代码不短也不长,直接全部贴上了吧

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if (mLayout == null) {
            defaultOnMeasure(widthSpec, heightSpec);
            return;
        }
        if (mLayout.isAutoMeasureEnabled()) {
            final int widthMode = MeasureSpec.getMode(widthSpec);
            final int heightMode = MeasureSpec.getMode(heightSpec);

            /**
             * This specific call should be considered deprecated and replaced with
             * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
             * break existing third party code but all documentation directs developers to not
             * override {@link LayoutManager#onMeasure(int, int)} when
             * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
             */
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

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

            if (mState.mLayoutStep == State.STEP_START) {
                dispatchLayoutStep1();
            }
            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
            // consistency
            mLayout.setMeasureSpecs(widthSpec, heightSpec);
            mState.mIsMeasuring = true;
            dispatchLayoutStep2();

            // now we can get the width and height from the children.
            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

            // if RecyclerView has non-exact width and height and if there is at least one child
            // which also has non-exact width & height, we have to re-measure.
            if (mLayout.shouldMeasureTwice()) {
                mLayout.setMeasureSpecs(
                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
                mState.mIsMeasuring = true;
                dispatchLayoutStep2();
                // now we can get the width and height from the children.
                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
            }
        } else {
            if (mHasFixedSize) {
                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
                return;
            }
            // custom onMeasure
            if (mAdapterUpdateDuringMeasure) {
                startInterceptRequestLayout();
                onEnterLayoutOrScroll();
                processAdapterUpdatesAndSetAnimationFlags();
                onExitLayoutOrScroll();

                if (mState.mRunPredictiveAnimations) {
                    mState.mInPreLayout = true;
                } else {
                    // consume remaining updates to provide a consistent state with the layout pass.
                    mAdapterHelper.consumeUpdatesInOnePass();
                    mState.mInPreLayout = false;
                }
                mAdapterUpdateDuringMeasure = false;
                stopInterceptRequestLayout(false);
            } else if (mState.mRunPredictiveAnimations) {
                // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
                // this means there is already an onMeasure() call performed to handle the pending
                // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
                // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
                // because getViewForPosition() will crash when LM uses a child to measure.
                setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
                return;
            }

            if (mAdapter != null) {
                mState.mItemCount = mAdapter.getItemCount();
            } else {
                mState.mItemCount = 0;
            }
            startInterceptRequestLayout();
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            stopInterceptRequestLayout(false);
            mState.mInPreLayout = false; // clear
        }
    }

老规矩,一行一行来看。

最开始会判断mLayout是不是null,也就是有没有设置LayoutManager,如果没有就调用defaultOnMeasure(widthSpec, heightSpec),就是只对RecyclerView进行measure,不处理子view,然后就返回了。当然我们这里有设置LayoutManager(此文章系列都是用LinearLayoutManager),所以接下来要判断mLayout.isAutoMeasureEnabled()返回值,这个isAutoMeasureEnabled方法有必要讲一下,因为注释有一大堆,注释那么多,肯定很重要,这里就翻译一下注释,也算是知道了这个方法的作用。

翻译如下:

首先这个方法的返回值指定了RecyclerView在measure的过程中是使用AutoMeasure机制还是让LayoutManager的onMeasure去完成measure工作,返回true表示使用AutoMeasure机制。默认情况下,LayoutManager会返回false,但是,它的子类都是返回true,包括LinearLayoutManager。当你确定要返回false的时候,LayoutManager的子类需要重写onMeasure方法,因为RecyclerView的measure工作会全权交个LayoutManager的onMeasure方法去做。如果返回true,则你实现的LayoutManager的子类不需要实现onMeasure方法。

所以这个AutoMeasure机制又是个啥东西呢?文档是这样介绍的:

AutoMeasure使用一种简单且令人满意的方法去处理RecyclerView的layout过程,包括被LayoutManager封装的子view的布局过程,也就是通过LayoutManager的onLayoutChildren方法来实现子view的布局,然后通过子view的尺寸和位置来确定RecyclerView的尺寸。更详细的定义如下:

  1. 在RecyclerView的onMeasure方法中,如果提供的长宽的mode都是EXACTLY,RecyclerView就直接设置指定的值,然后返回。
    1. 如果长和宽两者中有任何一个不是EXACTLY,RecyclerView就开始layout过程(WTF!在onMeasure方法中就就进行layout?事实证明确实
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值