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方法就分析完了!其實有一些代碼屬性也不怎麼清楚!畢竟裏裏面還是蠻複雜的,低耦合的代碼!

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