RecyclerView源码解析(-)-绘制流程

因为RecyclerView是继承ViewGroup的,所以这面从onMeasure(), draw(),onDraw(),onLayout()方法去看RecyclerView的绘制源码!

总体来说就是:在onMeasure()是否是自动测量
1,自动测量(现在几乎默认都是自动测量的)
(1),委托LayoutManager来测量RecyclerView的宽高(还是走defaultOnMeasure方法)拿到父view的spec进行设置尺寸,
(2),当前RecyclerView的宽高是否都为精确值,如果为精确值onMeasure直接reture,否则进行dispatchLayoutStep1(),dispatchLayoutStep2(),在通过addChildView之后重新计算RecyclerView的宽高!
但是这面的问题是return之后childView怎么添加呢,没事的因为在onLayout()方面里面会进行判断,如没addView会再一次的dispatchLayoutStep1(),dispatchLayoutStep2(),到dispatchLayoutStep3()。
(3),对于绘制,draw()和onDraw()分别绘制了ItemDecoration onDrawOver(),边界阴影和ItemDecoration onDraw()!

2,非自动测量
(1),就会完全靠layout绘制了

onMeasure()

protected void onMeasure(int widthSpec, int heightSpec) {
        //如果mLayout为空,直接会走默认的父view传递的Spec
        if (this.mLayout == null) {
            this.defaultOnMeasure(widthSpec, heightSpec);
        } else {
            //是否能自动测量(像LinearLayoutManager和剩余两个其实返回的都是true)
            if (!this.mLayout.isAutoMeasureEnabled()) {
                //是否有不变的大小,如果有不变的大小就直接设置measure并reture掉!
                if (this.mHasFixedSize) {
                    this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
                    return;
                }
                 //表明Adapter有数据变化的时候(changed,inserted,removed,moved)就会变为true
                if (this.mAdapterUpdateDuringMeasure) {
                    this.startInterceptRequestLayout();
                    this.onEnterLayoutOrScroll();
                    this.processAdapterUpdatesAndSetAnimationFlags();
                    this.onExitLayoutOrScroll();
                    if (this.mState.mRunPredictiveAnimations) {
                        this.mState.mInPreLayout = true;
                    } else {
                        this.mAdapterHelper.consumeUpdatesInOnePass();
                        this.mState.mInPreLayout = false;
                    }

                    this.mAdapterUpdateDuringMeasure = false;
                    this.stopInterceptRequestLayout(false);
                } else if (this.mState.mRunPredictiveAnimations) {
                    this.setMeasuredDimension(this.getMeasuredWidth(), this.getMeasuredHeight());
                    return;
                }

                if (this.mAdapter != null) {
                    this.mState.mItemCount = this.mAdapter.getItemCount();
                } else {
                    this.mState.mItemCount = 0;
                }

                this.startInterceptRequestLayout();
                //设置尺寸!
                this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
                this.stopInterceptRequestLayout(false);
                this.mState.mInPreLayout = false;
            } else {
                int widthMode = MeasureSpec.getMode(widthSpec);
                int heightMode = MeasureSpec.getMode(heightSpec);

                //委托LayoutManager来测量RecyclerView的宽高(还是走defaultOnMeasure方法)
                this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);

                //当前RecyclerView的宽高是否都为精确值
                boolean measureSpecModeIsExactly = widthMode == 1073741824 && heightMode == 1073741824;

               //如果RecyclerView的宽高都是写死的精确值或者是match_parent并且Adapter还没有设置就结束测量
               //因为前面的 this.mLayout.onMeasure()这一方法已经走了测量,因为有精确的尺寸,所以直接就reture掉了!
                if (measureSpecModeIsExactly || this.mAdapter == null) {
                    return;
                }
         
               //当宽高为wrap_content时,就先不能确定RecyclerView的宽高,因为需要先测量子itemView的宽高后才可以确定自己的宽高
                if (this.mState.mLayoutStep == 1) {
                    //做一些view信息存储等的准备工作.
                    this.dispatchLayoutStep1();
                }

                this.mLayout.setMeasureSpecs(widthSpec, heightSpec);
                this.mState.mIsMeasuring = true;
                
                //借助LayoutManager为RecyclerView添加view;
                this.dispatchLayoutStep2();

                //通过添加childView之后再一次计算RecyclerView的宽高!这样就确定了RecyclerView的宽高!
                this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
                
                //是否要进行第二次测量,如果进行第二次测量则再一次重新dispatchLayoutStep2();
                //setMeasuredDimensionFromChildren(widthSpec, heightSpec)
                if (this.mLayout.shouldMeasureTwice()) {
                    this.mLayout.setMeasureSpecs(MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), 1073741824), MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), 1073741824));
                    this.mState.mIsMeasuring = true;
                    this.dispatchLayoutStep2();
                    this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
                }
            }

        }
    }

仔细分析下,如果mLayout等于空,而mLayout就是LayoutManager,所以这面应该不是空的,所以会走到下面的else里面,而这时又会面临一个判断语句,mLayout.isAutoMeasureEnabled(),因为像LinearLayoutManager, GridLayoutManager及StaggeredGridLayoutManager都重写了
isAutoMeasureEnabled()这一方法,所以这面返回的应该都是true,这样就会走到下面的else{}语句里面。

 if (this.mState.mLayoutStep == 1) {
                    this.dispatchLayoutStep1();
                }

因为mLayoutStep 默认就是1,所以会走

private void dispatchLayoutStep1() {
        this.mState.assertLayoutStep(1);
        this.fillRemainingScrollValues(this.mState);
        this.mState.mIsMeasuring = false;
        this.startInterceptRequestLayout();
        this.mViewInfoStore.clear();
        this.onEnterLayoutOrScroll();
        this.processAdapterUpdatesAndSetAnimationFlags();
        this.saveFocusInfo();
        this.mState.mTrackOldChangeHolders = this.mState.mRunSimpleAnimations && this.mItemsChanged;
        this.mItemsAddedOrRemoved = this.mItemsChanged = false;
        this.mState.mInPreLayout = this.mState.mRunPredictiveAnimations;
        this.mState.mItemCount = this.mAdapter.getItemCount();
        this.findMinMaxChildLayoutPositions(this.mMinMaxLayoutPositions);
        int i;
        //是否要执行动画
        if (this.mState.mRunSimpleAnimations) {

            //拿到当前屏幕展示的childView数量,
            int count = this.mChildHelper.getChildCount();

            for(i = 0; i < count; ++i) {
                RecyclerView.ViewHolder holder = getChildViewHolderInt(this.mChildHelper.getChildAt(i));
                if (!holder.shouldIgnore() && (!holder.isInvalid() || this.mAdapter.hasStableIds())) {
                    RecyclerView.ItemAnimator.ItemHolderInfo animationInfo = this.mItemAnimator.recordPreLayoutInformation(this.mState, holder, RecyclerView.ItemAnimator.buildAdapterChangeFlagsForAnimations(holder), holder.getUnmodifiedPayloads());
                    // 存储预布局信息,也就是当前的没有进行动画的childView信息
                    this.mViewInfoStore.addToPreLayout(holder, animationInfo);
                    if (this.mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved() && !holder.shouldIgnore() && !holder.isInvalid()) {
                        long key = this.getChangedHolderKey(holder);
                        this.mViewInfoStore.addToOldChangeHolders(key, holder);
                    }
                }
            }
        } 
        
        //这面也是跟ItemAnim动画相关的操作,到时候itemAnimator会细说!大体上就是在this.mLayout.onLayoutChildren(this.mRecycler, this.mState)这个方法之后把不是preLayout的viewHolder加入集合中,
        //等待执行动画!     
        if (this.mState.mRunPredictiveAnimations) {
            this.saveOldPositions();
            boolean didStructureChange = this.mState.mStructureChanged;
            this.mState.mStructureChanged = false;
            this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
            this.mState.mStructureChanged = didStructureChange;

            for(i = 0; i < this.mChildHelper.getChildCount(); ++i) {
                View child = this.mChildHelper.getChildAt(i);
                RecyclerView.ViewHolder viewHolder = getChildViewHolderInt(child);
                if (!viewHolder.shouldIgnore() && !this.mViewInfoStore.isInPreLayout(viewHolder)) {
                    int flags = RecyclerView.ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
                    boolean wasHidden = viewHolder.hasAnyOfTheFlags(8192);
                    if (!wasHidden) {
                        flags |= 4096;
                    }

                    RecyclerView.ItemAnimator.ItemHolderInfo animationInfo = this.mItemAnimator.recordPreLayoutInformation(this.mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
                    if (wasHidden) {
                        this.recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
                    } else {
                        this.mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
                    }
                }
            }

            this.clearOldPositions();
        } else {
            this.clearOldPositions();
        }

        this.onExitLayoutOrScroll();
        this.stopInterceptRequestLayout(false);
        this.mState.mLayoutStep = 2;
    }

当measure的时候!一开始进行measure()的时候 this.mState.mRunSimpleAnimations 和 this.mState.mRunPredictiveAnimations肯定是false的,其实dispatchLayoutStep1()这个方法就是RecyclerView的pre layout阶段,用于记录数据集改变前子控件的信息(存储了一些信息到mViewInfoStore中)。
所以第一次onLayout的时候,肯定不会执行预布局。所以preLayout并不是在第一次measure和layout前所存在的状态,而是在数据集发生变化的时候,所应该具有的状态!

在回到所谓的onMeasure()方法中,继续往下走,会走到dispatchLayoutStep2();

 private void dispatchLayoutStep2() {
        this.startInterceptRequestLayout();
        this.onEnterLayoutOrScroll();
        this.mState.assertLayoutStep(6);
        this.mAdapterHelper.consumeUpdatesInOnePass();
        this.mState.mItemCount = this.mAdapter.getItemCount();
        this.mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
        this.mState.mInPreLayout = false;
        this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
        this.mState.mStructureChanged = false;
        this.mPendingSavedState = null;
        this.mState.mRunSimpleAnimations = this.mState.mRunSimpleAnimations && this.mItemAnimator != null;
        this.mState.mLayoutStep = 4;
        this.onExitLayoutOrScroll();
        this.stopInterceptRequestLayout(false);
    }

这面的方法主要还是
this.mState.mInPreLayout = false;
this.mLayout.onLayoutChildren(this.mRecycler, this.mState);

这面的onLayoutChildren(this.mRecycler, this.mState) 走的就是LayouManager的onLayoutChildren()方法!
进行视图的摆放与填充!

draw()
public void draw(Canvas c) {
        super.draw(c);
        int count = this.mItemDecorations.size();

        for(int i = 0; i < count; ++i) {
            ((RecyclerView.ItemDecoration)this.mItemDecorations.get(i)).onDrawOver(c, this, this.mState);
        }

        boolean needsInvalidate = false;
        int restore;
        int width;
        if (this.mLeftGlow != null && !this.mLeftGlow.isFinished()) {
            restore = c.save();
            width = this.mClipToPadding ? this.getPaddingBottom() : 0;
            c.rotate(270.0F);
            c.translate((float)(-this.getHeight() + width), 0.0F);
            needsInvalidate = this.mLeftGlow != null && this.mLeftGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (this.mTopGlow != null && !this.mTopGlow.isFinished()) {
            restore = c.save();
            if (this.mClipToPadding) {
                c.translate((float)this.getPaddingLeft(), (float)this.getPaddingTop());
            }

            needsInvalidate |= this.mTopGlow != null && this.mTopGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (this.mRightGlow != null && !this.mRightGlow.isFinished()) {
            restore = c.save();
            width = this.getWidth();
            int padding = this.mClipToPadding ? this.getPaddingTop() : 0;
            c.rotate(90.0F);
            c.translate((float)(-padding), (float)(-width));
            needsInvalidate |= this.mRightGlow != null && this.mRightGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (this.mBottomGlow != null && !this.mBottomGlow.isFinished()) {
            restore = c.save();
            c.rotate(180.0F);
            if (this.mClipToPadding) {
                c.translate((float)(-this.getWidth() + this.getPaddingRight()), (float)(-this.getHeight() + this.getPaddingBottom()));
            } else {
                c.translate((float)(-this.getWidth()), (float)(-this.getHeight()));
            }

            needsInvalidate |= this.mBottomGlow != null && this.mBottomGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (!needsInvalidate && this.mItemAnimator != null && this.mItemDecorations.size() > 0 && this.mItemAnimator.isRunning()) {
            needsInvalidate = true;
        }

        if (needsInvalidate) {
            ViewCompat.postInvalidateOnAnimation(this);
        }

    }
     for(int i = 0; i < count; ++i) {
            ((RecyclerView.ItemDecoration)this.mItemDecorations.get(i)).onDrawOver(c, this, this.mState);
        }

这段代码会绘制ItemDecoration中的onDrawOver内容,在绘制childView之后。

   if (this.mLeftGlow != null && !this.mLeftGlow.isFinished()) {
            restore = c.save();
            width = this.mClipToPadding ? this.getPaddingBottom() : 0;
            c.rotate(270.0F);
            c.translate((float)(-this.getHeight() + width), 0.0F);
            needsInvalidate = this.mLeftGlow != null && this.mLeftGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (this.mTopGlow != null && !this.mTopGlow.isFinished()) {
            restore = c.save();
            if (this.mClipToPadding) {
                c.translate((float)this.getPaddingLeft(), (float)this.getPaddingTop());
            }

            needsInvalidate |= this.mTopGlow != null && this.mTopGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (this.mRightGlow != null && !this.mRightGlow.isFinished()) {
            restore = c.save();
            width = this.getWidth();
            int padding = this.mClipToPadding ? this.getPaddingTop() : 0;
            c.rotate(90.0F);
            c.translate((float)(-padding), (float)(-width));
            needsInvalidate |= this.mRightGlow != null && this.mRightGlow.draw(c);
            c.restoreToCount(restore);
        }

        if (this.mBottomGlow != null && !this.mBottomGlow.isFinished()) {
            restore = c.save();
            c.rotate(180.0F);
            if (this.mClipToPadding) {
                c.translate((float)(-this.getWidth() + this.getPaddingRight()), (float)(-this.getHeight() + this.getPaddingBottom()));
            } else {
                c.translate((float)(-this.getWidth()), (float)(-this.getHeight()));
            }

            needsInvalidate |= this.mBottomGlow != null && this.mBottomGlow.draw(c);
            c.restoreToCount(restore);
        }

而这些代码都是用来绘制边界阴影效果的;

onDraw()

 public void onDraw(Canvas c) {
        super.onDraw(c);
        int count = this.mItemDecorations.size();

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

    }

其实onDraw没有处理什么逻辑~‘
主要是处理了ItemDecoration中的onDraw内容,在绘制childView之前。

onLayout()

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

这两段代码绘制性能分析会用到!
主要还是看dispatchLayout()这一方法

 void dispatchLayout() {
        if (this.mAdapter == null) {
            Log.e("RecyclerView", "No adapter attached; skipping layout");
        } else if (this.mLayout == null) {
            Log.e("RecyclerView", "No layout manager attached; skipping layout");
        } else {
            this.mState.mIsMeasuring = false;
            if (this.mState.mLayoutStep == 1) {
                this.dispatchLayoutStep1();
                this.mLayout.setExactMeasureSpecsFrom(this);
                this.dispatchLayoutStep2();
            } else if (!this.mAdapterHelper.hasUpdates() && this.mLayout.getWidth() == this.getWidth() && this.mLayout.getHeight() == this.getHeight()) {
                this.mLayout.setExactMeasureSpecsFrom(this);
            } else {
                this.mLayout.setExactMeasureSpecsFrom(this);
                this.dispatchLayoutStep2();
            }

            this.dispatchLayoutStep3();
        }
    }

这面主要是做了如果onMeasure()方法里面没有进行childView的添加,这面会进行添加,也满足了onMeasure()
方法中if()这一方法里面的布局填充!

dispatchLayoutStep3()这一方法主要是保存信息,触发动画,清除垃圾,

 private void dispatchLayoutStep3() {
        this.mState.assertLayoutStep(4);
        this.startInterceptRequestLayout();
        this.onEnterLayoutOrScroll();
        this.mState.mLayoutStep = 1;
        if (this.mState.mRunSimpleAnimations) {
            for(int i = this.mChildHelper.getChildCount() - 1; i >= 0; --i) {
                RecyclerView.ViewHolder holder = getChildViewHolderInt(this.mChildHelper.getChildAt(i));
                if (!holder.shouldIgnore()) {
                    long key = this.getChangedHolderKey(holder);
                    RecyclerView.ItemAnimator.ItemHolderInfo animationInfo = this.mItemAnimator.recordPostLayoutInformation(this.mState, holder);
                    RecyclerView.ViewHolder oldChangeViewHolder = this.mViewInfoStore.getFromOldChangeHolders(key);
                    if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
                        boolean oldDisappearing = this.mViewInfoStore.isDisappearing(oldChangeViewHolder);
                        boolean newDisappearing = this.mViewInfoStore.isDisappearing(holder);
                        if (oldDisappearing && oldChangeViewHolder == holder) {
                            //回归状态
                            this.mViewInfoStore.addToPostLayout(holder, animationInfo);
                        } else {
                            RecyclerView.ItemAnimator.ItemHolderInfo preInfo = this.mViewInfoStore.popFromPreLayout(oldChangeViewHolder);
                            this.mViewInfoStore.addToPostLayout(holder, animationInfo);
                            RecyclerView.ItemAnimator.ItemHolderInfo postInfo = this.mViewInfoStore.popFromPostLayout(holder);
                            if (preInfo == null) {
                                this.handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
                            } else {
                                //对于数据改变之后的动画有关代码!
                                this.animateChange(oldChangeViewHolder, holder, preInfo, postInfo, oldDisappearing, newDisappearing);
                            }
                        }
                    } else {
                        this.mViewInfoStore.addToPostLayout(holder, animationInfo);
                    }
                }
            }

            this.mViewInfoStore.process(this.mViewInfoProcessCallback);
        }

        this.mLayout.removeAndRecycleScrapInt(this.mRecycler);
        this.mState.mPreviousLayoutItemCount = this.mState.mItemCount;
        this.mDataSetHasChangedAfterLayout = false;
        this.mDispatchItemsChangedEvent = false;
        this.mState.mRunSimpleAnimations = false;
        this.mState.mRunPredictiveAnimations = false;
        this.mLayout.mRequestedSimpleAnimations = false;
        if (this.mRecycler.mChangedScrap != null) {
            this.mRecycler.mChangedScrap.clear();
        }

        if (this.mLayout.mPrefetchMaxObservedInInitialPrefetch) {
            this.mLayout.mPrefetchMaxCountObserved = 0;
            this.mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
            this.mRecycler.updateViewCacheSize();
        }

        this.mLayout.onLayoutCompleted(this.mState);
        this.onExitLayoutOrScroll();
        this.stopInterceptRequestLayout(false);
        this.mViewInfoStore.clear();
        if (this.didChildRangeChange(this.mMinMaxLayoutPositions[0], this.mMinMaxLayoutPositions[1])) {
            this.dispatchOnScrolled(0, 0);
        }

        this.recoverFocusFromState();
        this.resetFocusInfo();
    }
 this.mViewInfoStore.process(this.mViewInfoProcessCallback);

如上所示,当dispatchLayoutStep3() 这一方法for循环结束之后就会调用mViewInfoStore.process(this.mViewInfoProcessCallback)执行ItemAnimator动画! 剩下的代码在做一些回归的初始化!

这样的话RecyclerView的 onMeasure ,onDraw,onLayout方法就分析完了!其实有一些代码属性也不怎么清楚!毕竟里里面还是蛮复杂的,低耦合的代码!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章