RecyclerView 源碼分析

本文基于 V7-25.3.1

圖文總結(jié)

RecyclerView優(yōu)點(diǎn)

  • 更好的靈活配置能力劝术,在LayoutManager(布局)、Adapter(數(shù)據(jù)適配)和動畫的兼容上都更優(yōu)雅
  • 緩存能力增強(qiáng)绳泉,離屏緩存相對較優(yōu),另增加了一層緩存池緩存
  • 支持局部刷新拇勃,對于一些交互處理多的情況下,會帶來更好的性能

RecyclerView類圖

RecyclerView繪制流程圖

RecyclerView滑動流程圖

RecyclerView緩存介紹圖

源碼閱讀

RecyclerView使用方法分分析

RecyclerView 構(gòu)造方法

  • 進(jìn)行View相關(guān)配置屬性設(shè)置,觸摸范圍妓肢、滑動速度等
  • 設(shè)置Item動畫監(jiān)聽器
  • 初始化AdapterManager方面,創(chuàng)建AdapterHelper(負(fù)責(zé)Adapter里的數(shù)據(jù)集發(fā)生變化時(shí)的預(yù)處理操作)
  • 初始化ChildHelper(負(fù)責(zé)管理和訪問 RecyclerView 的子視圖)
  • 如果配置了LayoutManager 則通過反射方法創(chuàng)建它
 public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        ...
        // View配置相關(guān)屬性設(shè)置
        final ViewConfiguration vc = ViewConfiguration.get(context);
        mTouchSlop = vc.getScaledTouchSlop();
        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
        
        // 設(shè)置Item動畫監(jiān)聽器
        mItemAnimator.setListener(mItemAnimatorListener);
        // 設(shè)置 AdapterManager
        initAdapterManager();
        // 設(shè)置 ChildrenHelper 
        initChildrenHelper();
        // 硬件加速相關(guān)屬性設(shè)置
        if (ViewCompat.getImportantForAccessibility(this)
                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            ViewCompat.setImportantForAccessibility(this,
                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
        }
        mAccessibilityManager = (AccessibilityManager) getContext()
                .getSystemService(Context.ACCESSIBILITY_SERVICE);
        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
        
        // 如果attrs指定了LayoutManager,則創(chuàng)建LayoutManager
        boolean nestedScrollingEnabled = true;
        
        if (attrs != null) {
            int defStyleRes = 0;
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
                    defStyle, defStyleRes);
            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
            int descendantFocusability = a.getInt(
                    R.styleable.RecyclerView_android_descendantFocusability, -1);
            if (descendantFocusability == -1) {
                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            }
            a.recycle();
            // 反射方法創(chuàng)建 LayoutManager
            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);

            if (Build.VERSION.SDK_INT >= 21) {
                // SDK >=21下 孽尽,nestedScrollingEnabled狀態(tài)支持變更
                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
                        defStyle, defStyleRes);
                nestedScrollingEnabled = a.getBoolean(0, true);
                a.recycle();
            }
        } else {
            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        }

        // 重置nestedScrollingEnabled狀態(tài) SDK 21以下默認(rèn)true
        setNestedScrollingEnabled(nestedScrollingEnabled);
    }

setLayoutManager

  • 處理重新設(shè)置一個(gè)新的LayoutManager的一些邏輯
  • 設(shè)置this給到LayoutManager祥款,并如果attach了則執(zhí)行LayoutManger的attach分發(fā)實(shí)踐
  • 跟新緩存大小,并請求重新布局
public void setLayoutManager(LayoutManager layout) {
        if (layout == mLayout) {
            return;
        }
        stopScroll();
        // 設(shè)置新的layout情況下的一些處理邏輯
        ... 
        
        mChildHelper.removeAllViewsUnfiltered();
        mLayout = layout;
        if (layout != null) {
            // layout只能綁定一個(gè)mRecyclerView 
            if (layout.mRecyclerView != null) {
                throw new IllegalArgumentException("LayoutManager " + layout +
                        " is already attached to a RecyclerView: " + layout.mRecyclerView);
            }
            // 設(shè)置this引用給LayoutManager
            mLayout.setRecyclerView(this);
            if (mIsAttached) {
                // 分發(fā)attach事件
                mLayout.dispatchAttachedToWindow(this);
            }
        }
        // 重新更新緩存大小 及請求重新布局
        mRecycler.updateViewCacheSize();
        requestLayout();
    }

setAdapter

  • 接觸frozen狀態(tài)
  • 設(shè)置新的Adapter,并觸發(fā)一系列監(jiān)聽事件
public void setAdapter(Adapter adapter) {
   // 解除frozen狀態(tài)
   setLayoutFrozen(false);
   // 替換到當(dāng)前Adapter依痊,并觸發(fā)監(jiān)聽
   setAdapterInternal(adapter, false, true);
   requestLayout();
}

private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        // 舊Adapter進(jìn)行解綁數(shù)據(jù)監(jiān)聽 和 RecyclerView的引用
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }
        if (!compatibleWithPrevious || removeAndRecycleViews) {
            removeAndRecycleViews(); // 移除緩存的View
        }
        mAdapterHelper.reset();
        final Adapter oldAdapter = mAdapter;
        mAdapter = adapter;
        if (adapter != null) {
            // 處理新設(shè)置的Adapter的關(guān)聯(lián)監(jiān)聽和RecyclerView
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);
        }
        // 通知LayoutManager Adapter變更
        if (mLayout != null) {
            mLayout.onAdapterChanged(oldAdapter, mAdapter);
        }
        // 觸發(fā)Recycler Adapter變更事件
        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
        // 狀態(tài)置為 mStructureChanged 
        mState.mStructureChanged = true;
        markKnownViewsInvalid();
    }


RecyclerView繪制方法分析

onMeasure

  • 未賦值layoutManager情況下凉逛,走默認(rèn)measure衔沼,結(jié)果是無展示
  • 系統(tǒng)提供的LayoutManager默認(rèn)AutoMeasure。執(zhí)行LayoutManger的onMeasure方法
  • 如果未指定確定寬高的尺寸規(guī)格,則會進(jìn)行布局酬荞,繼而獲得子View的大小混巧。此過程可能執(zhí)行兩次

protected void onMeasure(int widthSpec, int heightSpec) {
   if (mLayout == null) {// 無LayoutManger
       defaultOnMeasure(widthSpec, heightSpec); 
       return;
   }
   // Android提供的三個(gè)LayoutManger,都是AutoMeasure
   if (mLayout.mAutoMeasure) {
       // 獲取測量規(guī)格
       final int widthMode = MeasureSpec.getMode(widthSpec);
       final int heightMode = MeasureSpec.getMode(heightSpec);
       final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
               && heightMode == MeasureSpec.EXACTLY;
       // 執(zhí)行LayoutManager的onMeasure方法
       mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
       
       if (skipMeasure || mAdapter == null) {
           return;
       }
       // 如果測量規(guī)格不確定 且設(shè)置了Adapter深员,則先執(zhí)行一次layout
       if (mState.mLayoutStep == State.STEP_START) {
           dispatchLayoutStep1();
       }
       // 設(shè)置測量規(guī)格
       mLayout.setMeasureSpecs(widthSpec, heightSpec);
       mState.mIsMeasuring = true;
       dispatchLayoutStep2();

       // 設(shè)置 獲取到子View的寬高
       mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

       //二次測量,寬高不確定情況下
       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 {
       ...
   }
}


RecyclerView.onLayout

  • 執(zhí)行DispatchLayout方法
  • 根據(jù)不同State狀態(tài),分別執(zhí)行Step1燎悍、Step2、Step3方法
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() {
        mState.mIsMeasuring = false;
        // 如果State狀態(tài)是 State.STEP_START 則執(zhí)行 Step1 和Step2
        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() 
            // 直接執(zhí)行 step2
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else {
            mLayout.setExactMeasureSpecsFrom(this);
        }
        // 執(zhí)行Step3 ,主要保存一些View信息和動畫執(zhí)行
        dispatchLayoutStep3();
    }

dispatchLayoutStep1

  • 第一步layout方法
  • 處理adapter變更
  • 確定需要執(zhí)行的動畫
  • 針對當(dāng)前的Views進(jìn)行信息緩存
  • 如有必要鸽粉,則進(jìn)行預(yù)布局并緩存信息
private void dispatchLayoutStep1() {
   //  State狀態(tài)斷言 
   mState.assertLayoutStep(State.STEP_START);
   mState.mIsMeasuring = false;
   // 是否過濾掉 RequestLayout執(zhí)行玷或,需要過濾的時(shí)候 執(zhí)行該方法蔬胯,會使過濾次數(shù)+1
   eatRequestLayout();
   // 清楚 ViewInfo 所有狀態(tài)和其存在的數(shù)據(jù)
   mViewInfoStore.clear();
   // 執(zhí)行進(jìn)入 layout或者scroll行為標(biāo)志
   onEnterLayoutOrScroll();
   // 執(zhí)行Adapter變更及計(jì)算那些需要執(zhí)行的動畫
   processAdapterUpdatesAndSetAnimationFlags();
   // 存儲焦點(diǎn)信息
   saveFocusInfo();
   // state狀態(tài)信息設(shè)置
   mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
   mItemsAddedOrRemoved = mItemsChanged = false;
   mState.mInPreLayout = mState.mRunPredictiveAnimations;
   mState.mItemCount = mAdapter.getItemCount();
   // 尋找 layout過程中position的最大和最小值
   findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   
   if (mState.mRunSimpleAnimations) {
       ...  
       int count = mChildHelper.getChildCount();
       for (int i = 0; i < count; ++i) {
            // 遍歷VieHolder
           final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
           if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
               continue;
           }
           // 創(chuàng)建 ItemHolderInfo
           final ItemHolderInfo animationInfo = mItemAnimator
                   .recordPreLayoutInformation(mState, holder,
                           ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
                           holder.getUnmodifiedPayloads());
           // mViewInfoStore存儲 holder及其對應(yīng)animation信息
           mViewInfoStore.addToPreLayout(holder, animationInfo);
           if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
               // 如果holder確定要更新舞竿,就把它添加到 oldChangeHolders 集合中
               mViewInfoStore.addToOldChangeHolders(key, holder);
           }
       }  
   }
   if (mState.mRunPredictiveAnimations) {
      ... // 運(yùn)行預(yù)布局
   }
   // 執(zhí)行退出 layout或者scroll行為標(biāo)志
   onExitLayoutOrScroll();
   // 對應(yīng) mEatRequestLayout -1
   resumeRequestLayout(false);
   // 狀態(tài)進(jìn)入 State.STEP_LAYOUT
   mState.mLayoutStep = State.STEP_LAYOUT;
}


private void processAdapterUpdatesAndSetAnimationFlags() {
   if (predictiveItemAnimationsEnabled()) {
       mAdapterHelper.preProcess(); // 預(yù)處理
   } else {
       mAdapterHelper.consumeUpdatesInOnePass();
   }
   boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
   // 計(jì)算 mRunSimpleAnimations 和 mRunPredictiveAnimations
   // mDataSetHasChangedAfterLayout 數(shù)據(jù)是否變化
   mState.mRunSimpleAnimations = mFirstLayoutComplete
           && mItemAnimator != null
           && (mDataSetHasChangedAfterLayout
           || animationTypeSupported
           || mLayout.mRequestedSimpleAnimations)
           && (!mDataSetHasChangedAfterLayout
           || mAdapter.hasStableIds());
   mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
           && animationTypeSupported
           && !mDataSetHasChangedAfterLayout
           && predictiveItemAnimationsEnabled();
}

dispatchLayoutStep2

  • 執(zhí)行最終的View布局操作,該過程由LayoutManager完成
  • 該方法可能會被多次執(zhí)行
private void dispatchLayoutStep2() {
   // 過濾掉 RequestLayout執(zhí)行米愿,需要過濾的時(shí)候 執(zhí)行該方法育苟,會使過濾次數(shù)+1。對應(yīng)resumeRequestLayout方法進(jìn)行消費(fèi)
   eatRequestLayout();
   // 對應(yīng) onExitLayoutOrScroll
   onEnterLayoutOrScroll();
   mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
   // 跳過預(yù)處理過程,一次性執(zhí)行完所有的update
   mAdapterHelper.consumeUpdatesInOnePass();
   mState.mItemCount = mAdapter.getItemCount(); // 賦值 itemCOunt
   mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; 

   
   mState.mInPreLayout = false;
   // 執(zhí)行 layout (執(zhí)行 LayoutManager 布局)
   mLayout.onLayoutChildren(mRecycler, mState);

   mState.mStructureChanged = false;
   mPendingSavedState = null;
   mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
   // State狀態(tài)進(jìn)入 State.STEP_ANIMATIONS
   mState.mLayoutStep = State.STEP_ANIMATIONS;
   // 對應(yīng) onExitLayoutOrScroll
   onExitLayoutOrScroll();
   // 對應(yīng)eatRequestLayout方法
   resumeRequestLayout(false);
}

dispatchLayoutStep3

layout過程最后一步,執(zhí)行相關(guān)動畫和一些清理事項(xiàng)

private void dispatchLayoutStep3() {
   ...
   if (mState.mRunSimpleAnimations) {
       // 執(zhí)行相關(guān)動畫
       ... 
   }
   // 一些清理動作   
}

draw

主要涉及Item裝飾的繪制和動畫

public void draw(Canvas c) {
   super.draw(c);

   final int count = mItemDecorations.size();
   for (int i = 0; i < count; i++) {
       mItemDecorations.get(i).onDrawOver(c, this, mState);
   }
  
   boolean needsInvalidate = false;
   ...
   if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
           mItemAnimator.isRunning()) {
       needsInvalidate = true;
   }

   if (needsInvalidate) {
       ViewCompat.postInvalidateOnAnimation(this);
   }
}
    
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);
   }
}

LinearLayoutManager 填充子View過程

LinearLayoutManager.onLayoutChildren

  • Child布局執(zhí)行核心方法
  • 布局方式万矾,通過確定錨點(diǎn),首先以錨點(diǎn)為基準(zhǔn)上到下布局慎框,在以錨點(diǎn)為基準(zhǔn)從下往上布局良狈。如果還有空間,繼續(xù)從上到下布局笨枯。最后確認(rèn)整個(gè)間隙是正確的薪丁。(反向布局及橫向反之則可)
  • 該方法為LayoutManager布局核心執(zhí)行方法严嗜,Child的測量和添加工作在fill這個(gè)重要方法執(zhí)行哮塞,接下來會闡述
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    
       // 確定是否需要反向布局
       // 確定錨點(diǎn)及偏移量 (1. 優(yōu)先焦點(diǎn)child 2. 如果是反向布局缓醋,則找recycler里面最最接近尾部的child 3. 如果是正向,則找最接近頭部的child)
       // 計(jì)算額外的偏移量(RecyclerView padding) 
       ...
       // 錨點(diǎn)準(zhǔn)備ready        
       onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
       // 臨時(shí) detach和回收當(dāng)前的view 第一次 measure 的時(shí)候不會產(chǎn)生效果胰蝠,因?yàn)榇藭r(shí) RecyclerView 還沒有子 View梗搅。 而在第二第三次 layout 時(shí),它會把子 View 從 RecyclerView 中 remove 或 detach 辱士,并緩存子 View鼠证,以便之后重新 add 回來或 attach 回來,避免重復(fù)加載相同的子 View
       detachAndScrapAttachedViews(recycler); 
       mLayoutState.mInfinite = resolveIsInfinite(); 
       mLayoutState.mIsPreLayout = state.isPreLayout();
       
       // 開始填充view
       if (mAnchorInfo.mLayoutFromEnd) {
          ... // 反向填充
       } else { // 正向填充
           // (基于錨點(diǎn)位置先 由上到下||由左到右)更新錨點(diǎn)信息
           updateLayoutStateToFillEnd(mAnchorInfo);
           mLayoutState.mExtra = extraForEnd; // 額外的尾部偏移量
           // 開始填充 View布局主要方法
           fill(recycler, mLayoutState, state, false);
           // 尾部位移
           endOffset = mLayoutState.mOffset;
           final int lastElement = mLayoutState.mCurrentPosition;
           if (mLayoutState.mAvailable > 0) {
               extraForStart += mLayoutState.mAvailable;
           }
           // (基于錨點(diǎn)位置 由下到上||由右到左)更新錨點(diǎn)信息
           updateLayoutStateToFillStart(mAnchorInfo);
           mLayoutState.mExtra = extraForStart;
           mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
           // 二次填充
           fill(recycler, mLayoutState, state, false);
           startOffset = mLayoutState.mOffset;
           // 仍有可用空間
           if (mLayoutState.mAvailable > 0) {
               extraForEnd = mLayoutState.mAvailable;
               // 繼續(xù) (基于錨點(diǎn)位置先 由上到下||由左到右)更新信息并填充View
               updateLayoutStateToFillEnd(lastElement, endOffset);
               mLayoutState.mExtra = extraForEnd;
               fill(recycler, mLayoutState, state, false);
               endOffset = mLayoutState.mOffset;
           }
       }
    
       // 有滑動位置導(dǎo)致的gap間隙修復(fù)處理
       ...
       // 預(yù)布局動畫處理
       ...
    }
        

LinearLayoutManager.fill

  • 如果是滑動流程,則根據(jù)情況進(jìn)行回收流程
  • LayoutState中部分成員變量含義米绕,mOffset:填充起始坐標(biāo)窿给,mCurrentPosition:填充起始數(shù)據(jù)的position靴寂,mAvailable:本次滑動可填充的距離,mScrollingOffset:滑動過的總量循環(huán)依次加載子View
  • 確定可布局大小缴守,直至布局大小消費(fèi)完成
  • 加載子View在 layoutChunk 中執(zhí)行
 int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
            
   // 可布局的位移
   final int start = layoutState.mAvailable;
   // 滑動偏移的情況下
   if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
       if (layoutState.mAvailable < 0) {
           layoutState.mScrollingOffset += layoutState.mAvailable;
       }
       // 執(zhí)行回收流程
       recycleByLayoutState(recycler, layoutState);
   }
   // 余量大小
   int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
   // 每次布局結(jié)果中間記錄 方便運(yùn)算  
   LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
   while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
       layoutChunkResult.resetInternal();
       // 加載子View
       layoutChunk(recycler, state, layoutState, layoutChunkResult);

       if (layoutChunkResult.mFinished) {
           break;
       }
       
       layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
       // 計(jì)算布局使用過的大小值
       if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
               || !state.isPreLayout()) {
           layoutState.mAvailable -= layoutChunkResult.mConsumed;
           remainingSpace -= layoutChunkResult.mConsumed;
       }
        // 如果當(dāng)前正在滾動屏幕
       if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
           layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
           if (layoutState.mAvailable < 0) {
               layoutState.mScrollingOffset += layoutState.mAvailable;
           }
           // 把移出屏幕的 View 緩存到 mCachedViews 里面
           recycleByLayoutState(recycler, layoutState);
       }
       if (stopOnFocusable && layoutChunkResult.mFocusable) {
           break;
       }
   }
   
   return start - layoutState.mAvailable;
}

LinearLayoutManager.layoutChunk

  • 通過layoutState.next(recycler)獲取目標(biāo)布局View
  • 獲取目標(biāo)View完畢后歇僧,進(jìn)行含裝飾的Margin計(jì)算苦酱,并執(zhí)行布局
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
            LayoutState layoutState, LayoutChunkResult result) {
        // 獲取下一個(gè)布局View (核心方法)
        View view = layoutState.next(recycler);
        
        LayoutParams params = (LayoutParams) view.getLayoutParams();
        if (layoutState.mScrapList == null) { // 除非特殊指定,否則mScrapList為null
            // 執(zhí)行 addView 
            if (mShouldReverseLayout == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                    // 添加到末尾
                addView(view); 
            } else {
                addView(view, 0); // 添加到第一個(gè)位置
            }
        } else {
            ...      
        }
        measureChildWithMargins(view, 0, 0); // 測量子View的Margins
        // 計(jì)算 含裝飾的Margin值的大小
        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
        int left, top, right, bottom;
        // 計(jì)算 r、l、t略吨、b的值
        if (mOrientation == VERTICAL) {
            if (isLayoutRTL()) {
                right = getWidth() - getPaddingRight();
                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
            } else {
                left = getPaddingLeft();
                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
            }
            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                bottom = layoutState.mOffset;
                top = layoutState.mOffset - result.mConsumed;
            } else {
                top = layoutState.mOffset;
                bottom = layoutState.mOffset + result.mConsumed;
            }
        } else {
            ...
        }
        // 對View進(jìn)行布局
        layoutDecoratedWithMargins(view, left, top, right, bottom);
        // 部分狀態(tài)改變 
        if (params.isItemRemoved() || params.isItemChanged()) {
            result.mIgnoreConsumed = true;
        }
        result.mFocusable = view.hasFocusable();
}

LinearLayoutManager.next

通過RecyclerView.Recycler獲取對應(yīng)Pos的View

View next(RecyclerView.Recycler recycler) {
  if (mScrapList != null) { // 除非定制View,不然為null
      return nextViewFromScrapList();
  }
  通過RecyclerView.Recycler 獲取目標(biāo)position對應(yīng)的View 
  final View view = recycler.getViewForPosition(mCurrentPosition);
  mCurrentPosition += mItemDirection; // 當(dāng)前pos 增加
  return view;
}

Recycler獲取VH的緩存和創(chuàng)建過程

Recycler.getViewForPosition

根據(jù)Pos獲取View方法匣砖,最終執(zhí)行tryGetViewHolderForPositionByDeadline獲取View

public View getViewForPosition(int position) {
  return getViewForPosition(position, false);
}

View getViewForPosition(int position, boolean dryRun) {
  return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}

Recycler.tryGetViewHolderForPositionByDeadline

  • 獲取ViewHolder方法
  • 如果是預(yù)布局,線通過ChangeScrap中獲取
  • 第一次嘗試獲取VH,依次從Scrap、Hidden登澜、Cache中獲取VH
  • 第二次嘗試獲取VH晃跺,針對具有StableId的Adapter莲趣,根據(jù)id依次從Scrap和Cache獲取
  • 第三次嘗試從自定義緩存中獲取VH
  • 第四次嘗試從Recycler獲取VH
  • 最后直接創(chuàng)建VH
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
  
  boolean fromScrapOrHiddenOrCache = false;
  ViewHolder holder = null;
  // 0) 如果是預(yù)布局溉仑, 從mChangedScrap中獲取 
  if (mState.isPreLayout()) {
      holder = getChangedScrapViewForPosition(position);
      fromScrapOrHiddenOrCache = holder != null;
  }
  // 1) 第一次嘗試獲取挖函,依次從Scrap、Hidden浊竟、Cache中獲取VH
  if (holder == null) {
        // 依次從Scrap怨喘、Hidden津畸、Cache中獲取VH
      holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
      ... 
  }
  if (holder == null) {
      final int offsetPosition = mAdapterHelper.findPositionOffset(position);
      final int type = mAdapter.getItemViewType(offsetPosition);
      // 2) 第二次嘗試獲取,當(dāng)Adapter具備StableIds情況
      if (mAdapter.hasStableIds()) {
          holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                  type, dryRun);
          if (holder != null) {
              // update position
              holder.mPosition = offsetPosition;
              fromScrapOrHiddenOrCache = true;
          }
      }
      // 3) 第三次嘗試從 自定義緩存獲取
      if (holder == null && mViewCacheExtension != null) {
                   final View view = mViewCacheExtension
                  .getViewForPositionAndType(this, position, type);
          if (view != null) {
              holder = getChildViewHolder(view);
              ...
          }
      }
      // 4) 第四次嘗試 從 RecyclerPool中獲取
      if (holder == null) { // fallback to pool
          holder = getRecycledViewPool().getRecycledView(type);
          ...
      }
      // 5) 開始創(chuàng)建
      if (holder == null) {
          long start = getNanoTime();
          // 創(chuàng)建VH
          holder = mAdapter.createViewHolder(RecyclerView.this, type);
          ... 
      }
      
       boolean bound = false;
       if (mState.isPreLayout() && holder.isBound()) {
           holder.mPreLayoutPosition = position;
       } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
            // 為bind過必怜,執(zhí)行bind方法
           final int offsetPosition = mAdapterHelper.findPositionOffset(position);
           bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
       }
}

        private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
                int position, long deadlineNs) {
            ...
            // 執(zhí)行Adapter bindViewHolder方法
            mAdapter.bindViewHolder(holder, offsetPosition);
            ...
            return true;
        }

00 從ChangeScrap獲取

針對的是預(yù)布局狀態(tài)肉拓,從mChangedScrap中獲取目標(biāo)ViewHolder
ScrapView:View仍然attach在其父RecyclerView上且可以被重復(fù)綁定數(shù)據(jù)及重復(fù)使用。將View標(biāo)記為Scrap過程中分為兩大類mAttachedScrap 和 mChangedScrap梳庆。
mAttachedScrap:VH有ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID這兩個(gè)Flag暖途,或者VH是沒有被更新過的,或者是可以被重新更新的VH靠益。
其它則是mChangedScrap

ViewHolder getChangedScrapViewForPosition(int position) {
  // 必須是預(yù)布局狀態(tài)丧肴,取mChangedScrap中的ViewHolder
  final int changedScrapSize;
  if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
      return null;
  }
  // 通過position獲取
  for (int i = 0; i < changedScrapSize; i++) {
      final ViewHolder holder = mChangedScrap.get(i);
      if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
          holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
          return holder;
      }
  }
  // 如果Adapter是固定id残揉,嘗試從Adapter獲取
  if (mAdapter.hasStableIds()) {
      final int offsetPosition = mAdapterHelper.findPositionOffset(position);
      if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
          final long id = mAdapter.getItemId(offsetPosition);
          for (int i = 0; i < changedScrapSize; i++) {
              final ViewHolder holder = mChangedScrap.get(i);
              if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
                  holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                  return holder;
              }
          }
      }
  }
  return null;
}

// Mark an attached view as scrap.
void scrapView(View view) {
       final ViewHolder holder = getChildViewHolderInt(view);
       if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
               || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
           holder.setScrapContainer(this, false);
           mAttachedScrap.add(holder);
       } else {
           if (mChangedScrap == null) {
               mChangedScrap = new ArrayList<ViewHolder>();
           }
           holder.setScrapContainer(this, true);
           mChangedScrap.add(holder);
       }
}


第一次嘗試獲取VH(AttachScrap胧后、Hidden、CacheView)

  • 先從 mAttachedScrap中獲取VH
  • 從隱藏且未移出的View中獲取 View
  • 從一級緩存CacheView中獲取
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
       final int scrapCount = mAttachedScrap.size();

       // 先從 mAttachedScrap中獲取VH
       for (int i = 0; i < scrapCount; i++) {
           final ViewHolder holder = mAttachedScrap.get(i);
           // 驗(yàn)證VH是否可用抱环,若可用壳快,則直接返回該VH
           if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                   && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
               holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
               return holder;
           }
       }
       // dryRun 傳遞是false(代表VH在scrap、cache中可以被Removed)
       if (!dryRun) {
           // 從隱藏且未移出的View中獲取 View
           View view = mChildHelper.findHiddenNonRemovedView(position);
           if (view != null) {
               // View可用镇草,則進(jìn)行可視眶痰、detach、scrap緩存
               final ViewHolder vh = getChildViewHolderInt(view);
               mChildHelper.unhide(view);
               int layoutIndex = mChildHelper.indexOfChild(view);
               mChildHelper.detachViewFromParent(layoutIndex);
               scrapView(view);
               vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
                       | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
               return vh;
           }
       }

       // 從第一級緩存View中獲取
       final int cacheSize = mCachedViews.size();
       for (int i = 0; i < cacheSize; i++) {
           final ViewHolder holder = mCachedViews.get(i);
           // VH是有效的
           if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
               if (!dryRun) {
                   mCachedViews.remove(i); // 移出獲取的cache
               }
               return holder; // 返回VH
           }
       }
       return null;
}

第二次嘗試獲取VH(Adapter有穩(wěn)定id情況)

  • Adapter配置的id是穩(wěn)定的梯啤,穩(wěn)定指數(shù)據(jù)集變化的時(shí)候竖伯,對于同一數(shù)據(jù)對應(yīng)的id是唯一的
  • 先嘗試從Scrap獲取VH,非dryRun下因宇,將未命中的從Scrap中移出七婴,并加入到Cache或Pool緩存
  • 在嘗試從Cache獲取VH,將未命中的從Cache中移出察滑,并加入到Pool緩存
ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
       // 從AttachedScrap中 嘗試獲取VH
       final int count = mAttachedScrap.size();
       for (int i = count - 1; i >= 0; i--) {
           final ViewHolder holder = mAttachedScrap.get(i);
           // id 相等 且 holder 非Scrap返回
           if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
               if (type == holder.getItemViewType()) {
                   holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                   if (holder.isRemoved()) {
                        // 從事
                       if (!mState.isPreLayout()) {
                           holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
                                   | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
                       }
                   }
                   return holder;
               } else if (!dryRun) {
                   // 從AttachedScrap移除
                   mAttachedScrap.remove(i);
                   removeDetachedView(holder.itemView, false);
                   // 回收加入至 cache 或者 pool
                   quickRecycleScrapView(holder.itemView);
               }
           }
       }

       // 從CacheView中嘗試獲取
       final int cacheSize = mCachedViews.size();
       for (int i = cacheSize - 1; i >= 0; i--) {
           final ViewHolder holder = mCachedViews.get(i);
           if (holder.getItemId() == id) {
               if (type == holder.getItemViewType()) {
                   if (!dryRun) {
                       mCachedViews.remove(i); // 從Cache中移出
                   }
                   return holder;
               } else if (!dryRun) {
                   // 從Cache中移出打厘,放到pool中
                   recycleCachedViewAt(i);
                   return null;
               }
           }
       }
       return null;
}


RecyclerView滑動機(jī)制分析

根據(jù)View事件機(jī)制可以直接來看onTouchEvent方法。
重點(diǎn)查看move事件贺辰。move事件執(zhí)行了scrollByInternal方法户盯。該方法最后會執(zhí)行LayoutManager的Scroll方法,以LinearLayoutManager為例饲化,它的ScrollBy方法最終執(zhí)行到fill方法莽鸭。也就是上文提到的ItemView填充方法,滑動過程中會不斷執(zhí)行獲取對應(yīng)位置的ViewHolder吃靠,然后進(jìn)行View的展示硫眨。從而實(shí)現(xiàn)RecyclerView的滑動

public boolean onTouchEvent(MotionEvent e) {
   ...

   switch (action) {
       case MotionEvent.ACTION_DOWN: {
          ...
       case MotionEventCompat.ACTION_POINTER_DOWN: 
          ...

       case MotionEvent.ACTION_MOVE: { // 觸摸時(shí)間-move
           ...
           if (mScrollState == SCROLL_STATE_DRAGGING) {
               mLastTouchX = x - mScrollOffset[0];
               mLastTouchY = y - mScrollOffset[1];
                // 執(zhí)行內(nèi)部滑動方法
               if (scrollByInternal(
                       canScrollHorizontally ? dx : 0,
                       canScrollVertically ? dy : 0,
                       vtev)) {
                   getParent().requestDisallowInterceptTouchEvent(true);
               }
               ...           }
       } break;

       case MotionEventCompat.ACTION_POINTER_UP: {
           onPointerUp(e);
       } break;

       case MotionEvent.ACTION_UP: { // 觸摸事件-up
           // 執(zhí)行 fling方法 ,主要做一些item和scroller動畫等操作
           if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
               setScrollState(SCROLL_STATE_IDLE);
           }
           resetTouch();
       } break;

       case MotionEvent.ACTION_CANCEL: {
           cancelTouch();
       } break;
   }
   ...
   return true;
}

scrollByInternal

  • 內(nèi)部Scroll執(zhí)行方法撩笆,此處會執(zhí)行LayoutManager的Scroll方法
  • 其它處罰Nested捺球、OnScroll等事件

boolean scrollByInternal(int x, int y, MotionEvent ev) {
   int unconsumedX = 0, unconsumedY = 0;
   int consumedX = 0, consumedY = 0;

   consumePendingUpdateOperations();
   if (mAdapter != null) {
       eatRequestLayout();
       onEnterLayoutOrScroll();
       TraceCompat.beginSection(TRACE_SCROLL_TAG);
       if (x != 0) {
           consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
           unconsumedX = x - consumedX;
       }
       if (y != 0) {
            // LinearLayout 豎向布局為例缸浦,走LayoutManager滑動放啊放
           consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
           unconsumedY = y - consumedY;
       }
       TraceCompat.endSection();
       repositionShadowingViews();
       onExitLayoutOrScroll();
       resumeRequestLayout(false);
   }
   if (!mItemDecorations.isEmpty()) {
       invalidate();
   }
    // 分發(fā) NestedScroll事件
   if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
    ...
   }
   if (consumedX != 0 || consumedY != 0) {
       dispatchOnScrolled(consumedX, consumedY); // 分發(fā)onScrolled事件
   }
   if (!awakenScrollBars()) {
       invalidate();
   }
   return consumedX != 0 || consumedY != 0;
}

LinearLayoutManager執(zhí)行滑動處理

  • 執(zhí)行scrollBy方法
  • scrollBy方法最終走到 fill方法(上面提到的填充子View方法)
  • 該方法則會進(jìn)行 ItemView的填充。從而完成Recycler滑動時(shí)氮兵,View的重新創(chuàng)建或者重新綁定一系列過程
  • 平移整個(gè)View的child裂逐,實(shí)現(xiàn)滑動效果
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
       RecyclerView.State state) {
   if (mOrientation == HORIZONTAL) {
       return 0;
   }
   return scrollBy(dy, recycler, state);
}


int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
   ...
   mLayoutState.mRecycle = true;
   ensureLayoutState();
   final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
   final int absDy = Math.abs(dy);
   // 更新LayoutState,布局方向和偏移值泣栈。目的是讓LayoutManager知道從開始還是末尾進(jìn)行回收和填充
   updateLayoutState(layoutDirection, absDy, true, state);
   
   // 執(zhí)行 LinearLayout的fill 方法
   final int consumed = mLayoutState.mScrollingOffset
           + fill(recycler, mLayoutState, state, false);
   ...
   // 平移整個(gè)view的child
   mOrientationHelper.offsetChildren(-scrolled);
   
   return scrolled;
}
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
    ...
    // 執(zhí)行回收流程
    recycleByLayoutState(recycler, layoutState);
    ...
    // 執(zhí)行填充流程(參考上面layoutChunk方法)
    layoutChunk(recycler, state, layoutState,layoutChunkResult);
}

LinearLayoutManager回收流程

  • 根據(jù)不同的布局方向進(jìn)行不同方向的回收卜高。以Start為例介紹
  • 計(jì)算位移limit值,根據(jù)limit
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
   // 假設(shè)是 初始方向布局南片,則開始末尾View回收掺涛。反之亦然
   if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
       recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
   } else {
       recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
   }
}

private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
   final int limit = dt;
   final int childCount = getChildCount();
   if (mShouldReverseLayout) {
       ...
   } else {
        for (int i = 0; i < childCount; i++) {
           View child = getChildAt(i);
           // 遍歷child,當(dāng)超過限制大小時(shí)候疼进,開始回收
           if (mOrientationHelper.getDecoratedEnd(child) > limit
                   || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
               recycleChildren(recycler, 0, i); // 執(zhí)行Children回收流程
               return;
           }
       }        }
}
    
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
   if (endIndex > startIndex) {
       for (int i = endIndex - 1; i >= startIndex; i--) {
           removeAndRecycleViewAt(i, recycler); // 執(zhí)行RecyclerView的移出和回收方法
       }
   } else {
       for (int i = startIndex; i > endIndex; i--) {
           removeAndRecycleViewAt(i, recycler);
       }
   }
}

RecyclerView.removeAndRecycleViewAt

  • 移出和回收View方法
  • 執(zhí)行ChildHelper的移出View方法薪缆。內(nèi)部Bucket移出和回掉CallBack進(jìn)行View移出
  • 執(zhí)行Recycler回收方法
public void removeAndRecycleViewAt(int index, Recycler recycler) {
  final View view = getChildAt(index); // 獲取目標(biāo)View
  removeViewAt(index); // 執(zhí)行ChildHelper移出
  recycler.recycleView(view); // 回收View
}

public void removeViewAt(int index) {
  final View child = getChildAt(index); 
  if (child != null) {
      mChildHelper.removeViewAt(index); // 執(zhí)行ChildHelper移出
  }
}

public void recycleView(View view) {
  ViewHolder holder = getChildViewHolderInt(view); // 獲取VH
  // ViewHolder 回收前,需要完全detach伞广、且不是Scrap
  if (holder.isTmpDetached()) {
      removeDetachedView(view, false); 
  }
  if (holder.isScrap()) {
      holder.unScrap();
  } else if (holder.wasReturnedFromScrap()){
      holder.clearReturnedFromScrapFlag();
  }
  recycleViewHolderInternal(holder); // 執(zhí)行回收
}



RecyclerView.recycleViewHolderInternal

  • 內(nèi)部緩存VH方法
  • 如果CacheView滿了拣帽,則移出一個(gè)Cache到Pool中
  • 將目標(biāo)VH緩存到Cache末尾
  • 如果沒有Cache成功,則直接緩存到Pool中
 void recycleViewHolderInternal(ViewHolder holder) {  
      if (mViewCacheMax > 0
              && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
              | ViewHolder.FLAG_REMOVED
              | ViewHolder.FLAG_UPDATE
              | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
          // Cache緩存?zhèn)€數(shù)超了嚼锄,則直接回收CacheView到RecyclerPool
          int cachedViewSize = mCachedViews.size();
          if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
              recycleCachedViewAt(0);
              cachedViewSize--;
          }

          int targetCacheIndex = cachedViewSize;
          // 將VH緩存到CacheView中
          mCachedViews.add(targetCacheIndex, holder);
          cached = true;
      }
      // 如果未CacheView緩存减拭,則直接緩存RecyclerViewPool中
      if (!cached) {
          addViewHolderToRecycledViewPool(holder, true);
          recycled = true;
      }
       ...
}

局部刷新

Adapter數(shù)據(jù)操作對外API

RecyclerView.Adapter提供局部數(shù)據(jù)變化通知方法,然后執(zhí)行到RecyclerViewDataObserver對應(yīng)的各種數(shù)據(jù)操作方法上区丑。

RecyclerViewDataObserver

  • 通過mAdapterHelper進(jìn)行數(shù)據(jù)變化處理操作
  • 然后觸發(fā)更新處理
  • 下面介紹下 ItemChanged操作
   public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
       assertNotInLayoutOrScroll(null);
       if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
           triggerUpdateProcessor();
       }
   }

   public void onItemRangeInserted(int positionStart, int itemCount) {
       assertNotInLayoutOrScroll(null);
       if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
           triggerUpdateProcessor();
       }
   }

   public void onItemRangeRemoved(int positionStart, int itemCount) {
       assertNotInLayoutOrScroll(null);
       if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
           triggerUpdateProcessor();
       }
   }

AdapterHelper.onItemRangeChanged

boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   // 添加一個(gè)更新操作 拧粪,標(biāo)志為update、記錄pos沧侥、item相關(guān)信息
   mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
   mExistingUpdateTypes |= UpdateOp.UPDATE;
   // 如果只有一個(gè)待處理操作則為true可霎,true則執(zhí)行后續(xù)更新處理。如果是多個(gè)正什,則會忽略啥纸,因?yàn)樵诘谝淮纬霭l(fā)后,就會集中處理
   return mPendingUpdates.size() == 1;
}

RecyclerViewDataObserver.triggerUpdateProcessor

  1. 當(dāng)RecyclerView有固定大小婴氮,且已經(jīng)Attached了斯棒。則走Runnable更新
  2. 否則直接走requestLayout方式更新,即重新走繪制流程 onMeasure主经、onLayout等
void triggerUpdateProcessor() {
  if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
      // RecyclerView有固定大小的時(shí)候 會執(zhí)行mUpdateChildViewsRunnable 來處理更新
      ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
  } else {
      // 直接走 requestLayout方式來處理
      mAdapterUpdateDuringMeasure = true;
      requestLayout();
  }
}

triggerUpdateProcessor下requestLayout

requestLayout下 onMeasure -> dispatchLayout -> dispatchLayoutStep2 -> layoutChildren -> fill -> layoutChunk -> next -> tryGetViewHolderForPositionByDeadline
最終對Item進(jìn)行重新綁定 實(shí)現(xiàn)局部刷新邏輯

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
                
           if (mState.isPreLayout() && holder.isBound()) {
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                // 執(zhí)行數(shù)據(jù)變化的Holder的重新bind荣暮,從而實(shí)現(xiàn)局部刷新              
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
            }
      
                
}

triggerUpdateProcessor下mUpdateChildViewsRunnable

當(dāng)RecyclerView有固定大小時(shí),則不需要Measure罩驻,直接走dispatchLayout方法進(jìn)行刷新操作

final Runnable mUpdateChildViewsRunnable = new Runnable() {
   @Override
   public void run() {
        ...
        // 消費(fèi) 等待執(zhí)行的操作 
        consumePendingUpdateOperations();
   }
}

void consumePendingUpdateOperations() {
        
      if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
           .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
                   | AdapterHelper.UpdateOp.MOVE)) {
        // update 情況下 邏輯
       
       eatRequestLayout();
       onEnterLayoutOrScroll();
       // 數(shù)據(jù)預(yù)處理 
       mAdapterHelper.preProcess();
       if (!mLayoutRequestEaten) {
            // 執(zhí)行 dispatchLayout 進(jìn)行局部刷新處理
           if (hasUpdatedView()) {
               dispatchLayout();
           } else {
               // no need to layout, clean state
               mAdapterHelper.consumePostponedUpdates();
           }
       }
       ...
   } else if (mAdapterHelper.hasPendingUpdates()) {
       // add穗酥、remove等操作,直接執(zhí)行dispatchLayout
       dispatchLayout();
       TraceCompat.endSection();
   }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市砾跃,隨后出現(xiàn)的幾起案子骏啰,更是在濱河造成了極大的恐慌,老刑警劉巖抽高,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件判耕,死亡現(xiàn)場離奇詭異,居然都是意外死亡翘骂,警方通過查閱死者的電腦和手機(jī)壁熄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碳竟,“玉大人草丧,你說我怎么就攤上這事∮ㄎΓ” “怎么了昌执?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長统翩。 經(jīng)常有香客問我仙蚜,道長,這世上最難降的妖魔是什么厂汗? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮呜师,結(jié)果婚禮上娶桦,老公的妹妹穿的比我還像新娘。我一直安慰自己汁汗,他們只是感情好衷畦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著知牌,像睡著了一般祈争。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上角寸,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天菩混,我揣著相機(jī)與錄音,去河邊找鬼扁藕。 笑死沮峡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亿柑。 我是一名探鬼主播邢疙,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了疟游?” 一聲冷哼從身側(cè)響起呼畸,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颁虐,沒想到半個(gè)月后役耕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聪廉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年瞬痘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片板熊。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡框全,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出干签,到底是詐尸還是另有隱情津辩,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布容劳,位于F島的核電站喘沿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏竭贩。R本人自食惡果不足惜蚜印,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望留量。 院中可真熱鬧窄赋,春花似錦、人聲如沸楼熄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽可岂。三九已至错敢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缕粹,已是汗流浹背稚茅。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留致开,地道東北人峰锁。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像双戳,于是被迫代替她去往敵國和親虹蒋。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容