前言: 當前市場上有不少成熟的RecyclerView分析文章,但那始終是其餘人總結出來的,還得本身動手分析,才知道本身理解了有多少,固然這個也算是加深對RecyclerView對理解吧;android
官方簡介:A flexible view for providing a limited window into a large data set.
一種靈活的視圖,在有限的窗口,展現大量的數據集;
在開始以前,爲了加深理解,咱們須要帶着疑問進行閱讀;緩存
(1)RecyclerView是怎麼加載數據的?架構
(2)RecyclerView是怎麼將View繪製到頁面上的?app
(3)RecyclerView是怎麼複用item的?ide
由上圖可知,RecyclerView主要由這幾部分組成;那他們的關係是啥呢? 具體是如何關聯的呢?且聽完細細道來!oop
數據層面:首頁RecyclerView須要將數據和view綁定起來,是經過Adapter加載ViewHolder來實現綁定數據的;
佈局層面:RecyclerView的Item的佈局是經過LayoutManager來進行佈局的;
複用層面:LayoutManger從Recycler獲取item來進行復用;源碼分析
1,Adapter:將數據轉化爲RecyclerView能夠識別的數據;佈局
2,ViewHolder:將數據和item綁定起來;post
3,LayoutManager:經過計算將Item佈局到頁面中;測試
4,Recycler:複用機制,統一管理Item,用於複用;
5,ItemDecoration:繪製item的樣式;
首先,先來看看RecyclerView 的初始化流程,先舉個簡單的例子;
//獲取RecyclerView 控件 RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview); //建立adapter MyAdapter adapter = new MyAdapter(list); //建立LayoutManager LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); //設置LayoutManager recyclerView.setLayoutManager(linearLayoutManager); //設置Adapter recyclerView.setAdapter(adapter);
1,咱們先來看看RecyclerView 的構造方法作了啥?
public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //建立觀察者 this.mObserver = new RecyclerView.RecyclerViewDataObserver(); //建立回收器 this.mRecycler = new RecyclerView.Recycler(); //建立佈局信息保存類 this.mViewInfoStore = new ViewInfoStore(); this.mUpdateChildViewsRunnable = new Runnable() { public void run() { if (RecyclerView.this.mFirstLayoutComplete && !RecyclerView.this.isLayoutRequested()) { if (!RecyclerView.this.mIsAttached) { RecyclerView.this.requestLayout(); } else if (RecyclerView.this.mLayoutFrozen) { RecyclerView.this.mLayoutWasDefered = true; } else { RecyclerView.this.consumePendingUpdateOperations(); } } } }; ... if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0); this.mClipToPadding = a.getBoolean(0, true); a.recycle(); } else { this.mClipToPadding = true; } ... this.mAccessibilityManager = (AccessibilityManager)this.getContext().getSystemService("accessibility"); this.setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this)); boolean nestedScrollingEnabled = true; if (attrs != null) { int defStyleRes = 0; TypedArray a = context.obtainStyledAttributes(attrs, styleable.RecyclerView, defStyle, defStyleRes); //從佈局文件獲取Layoutmanger的名稱 String layoutManagerName = a.getString(styleable.RecyclerView_layoutManager); int descendantFocusability = a.getInt(styleable.RecyclerView_android_descendantFocusability, -1); if (descendantFocusability == -1) { this.setDescendantFocusability(262144); } this.mEnableFastScroller = a.getBoolean(styleable.RecyclerView_fastScrollEnabled, false); //經過layoutManger的名稱進行反射建立layoutManager,並設置給RecycleView this.createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes); ... } else { this.setDescendantFocusability(262144); } //設置是否支持嵌套滾動,默認爲true this.setNestedScrollingEnabled(nestedScrollingEnabled); }
從構造方法能夠看出,裏面作了一大堆初始化的操做,最主要看一下這個建立layoutManager的方法createLayoutManager();
根據佈局屬性進行反射來建立layoutManager;
private void createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes) { if (className != null) { className = className.trim(); if (!className.isEmpty()) { className = this.getFullClassName(context, className); try { ClassLoader classLoader; if (this.isInEditMode()) { classLoader = this.getClass().getClassLoader(); } else { classLoader = context.getClassLoader(); } Class<? extends RecyclerView.LayoutManager> layoutManagerClass = classLoader.loadClass(className).asSubclass(RecyclerView.LayoutManager.class); Object[] constructorArgs = null; Constructor constructor; try { //經過反射建立佈局構造器 constructor = layoutManagerClass.getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE); constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes}; } catch (NoSuchMethodException var13) { try { constructor = layoutManagerClass.getConstructor(); } catch (NoSuchMethodException var12) { var12.initCause(var13); throw new IllegalStateException(attrs.getPositionDescription() + ": Error creating LayoutManager " + className, var12); } } constructor.setAccessible(true); //將建立出來的LayoutManger設置給RecycleView this.setLayoutManager((RecyclerView.LayoutManager)constructor.newInstance(constructorArgs)); } catch (ClassNotFoundException var14) { throw new IllegalStateException(attrs.getPositionDescription() + ": Unable to find LayoutManager " + className, var14); } catch (InvocationTargetException var15) { throw new IllegalStateException(attrs.getPositionDescription() + ": Could not instantiate the LayoutManager: " + className, var15); } catch (InstantiationException var16) { throw new IllegalStateException(attrs.getPositionDescription() + ": Could not instantiate the LayoutManager: " + className, var16); } catch (IllegalAccessException var17) { throw new IllegalStateException(attrs.getPositionDescription() + ": Cannot access non-public constructor " + className, var17); } catch (ClassCastException var18) { throw new IllegalStateException(attrs.getPositionDescription() + ": Class is not a LayoutManager " + className, var18); } } } }
再看一下setLayoutManager()這個方法裏面作了啥操做?
public void setLayoutManager(@Nullable RecyclerView.LayoutManager layout) { if (layout != this.mLayout) { //中止當前的滾動操做 this.stopScroll(); if (this.mLayout != null) { //判斷當前的layoutManager若是爲空,則將該layoutManager的狀態進行初始化; if (this.mItemAnimator != null) { this.mItemAnimator.endAnimations(); } this.mLayout.removeAndRecycleAllViews(this.mRecycler); this.mLayout.removeAndRecycleScrapInt(this.mRecycler); this.mRecycler.clear(); if (this.mIsAttached) { this.mLayout.dispatchDetachedFromWindow(this, this.mRecycler); } this.mLayout.setRecyclerView((RecyclerView)null); this.mLayout = null; } else { this.mRecycler.clear(); } this.mChildHelper.removeAllViewsUnfiltered(); //將當前的layoutManager賦值給成員變量 this.mLayout = layout; if (layout != null) { if (layout.mRecyclerView != null) { throw new IllegalArgumentException("LayoutManager " + layout + " is already attached to a RecyclerView:" + layout.mRecyclerView.exceptionLabel()); } //將當前的RecyclerView賦值給layoutManager this.mLayout.setRecyclerView(this); if (this.mIsAttached) { this.mLayout.dispatchAttachedToWindow(this); } } //更新一下RecyclerView的緩存 this.mRecycler.updateViewCacheSize(); //觸發從新佈局 this.requestLayout(); } }
總結:看完RecyclerView的構造方法,裏面主要是作了一些初始化的操做,並建立了layoutManager設置給RecyclerView(若是佈局屬性有設置的話);
2,看完了RecyclerView的setLayoutManager()的流程,咱們繼續接着分析,看一下setAdapter()具體作了啥?
public void setAdapter(@Nullable RecyclerView.Adapter adapter) { this.setLayoutFrozen(false); //主要模塊 this.setAdapterInternal(adapter, false, true); this.processDataSetCompletelyChanged(false); this.requestLayout(); }
跟進源碼,咱們主要分析setAdapterInternal()這個方法,讓咱們看看這個源碼裏面作了什麼操做;
private void setAdapterInternal(@Nullable RecyclerView.Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) { if (this.mAdapter != null) { //解註冊以前的數據觀察者 this.mAdapter.unregisterAdapterDataObserver(this.mObserver); this.mAdapter.onDetachedFromRecyclerView(this); } if (!compatibleWithPrevious || removeAndRecycleViews) { //進行初始化操做,初始化layoutManger,初始化mRecycler this.removeAndRecycleViews(); } this.mAdapterHelper.reset(); RecyclerView.Adapter oldAdapter = this.mAdapter; //將adapter賦值給當前成員變量 this.mAdapter = adapter; if (adapter != null) { //adapter註冊數據觀察者,用於監聽數據的增刪改查 adapter.registerAdapterDataObserver(this.mObserver); adapter.onAttachedToRecyclerView(this); } if (this.mLayout != null) { this.mLayout.onAdapterChanged(oldAdapter, this.mAdapter); } this.mRecycler.onAdapterChanged(oldAdapter, this.mAdapter, compatibleWithPrevious); this.mState.mStructureChanged = true; }
這個方法裏面主要是給adapter註冊數據監聽,用於數據的增刪改查的刷新,並作一些初始化的操做;
咱們再看一下這個觀察者裏面主要作了什麼操做,具體的實現是在RecyclerViewDataObserver 這個類裏面;
private class RecyclerViewDataObserver extends RecyclerView.AdapterDataObserver { RecyclerViewDataObserver() { } public void onChanged() { RecyclerView.this.assertNotInLayoutOrScroll((String)null); RecyclerView.this.mState.mStructureChanged = true; RecyclerView.this.processDataSetCompletelyChanged(true); if (!RecyclerView.this.mAdapterHelper.hasPendingUpdates()) { RecyclerView.this.requestLayout(); } } public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { RecyclerView.this.assertNotInLayoutOrScroll((String)null); if (RecyclerView.this.mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) { this.triggerUpdateProcessor(); } } public void onItemRangeInserted(int positionStart, int itemCount) { RecyclerView.this.assertNotInLayoutOrScroll((String)null); if (RecyclerView.this.mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) { this.triggerUpdateProcessor(); } } public void onItemRangeRemoved(int positionStart, int itemCount) { RecyclerView.this.assertNotInLayoutOrScroll((String)null); if (RecyclerView.this.mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) { this.triggerUpdateProcessor(); } } public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { RecyclerView.this.assertNotInLayoutOrScroll((String)null); if (RecyclerView.this.mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) { this.triggerUpdateProcessor(); } } void triggerUpdateProcessor() { if (RecyclerView.POST_UPDATES_ON_ANIMATION && RecyclerView.this.mHasFixedSize && RecyclerView.this.mIsAttached) { ViewCompat.postOnAnimation(RecyclerView.this, RecyclerView.this.mUpdateChildViewsRunnable); } else { RecyclerView.this.mAdapterUpdateDuringMeasure = true; RecyclerView.this.requestLayout(); } } }
看到了咱們很熟悉的方法,即adapter刷新數據所調用的方法;咱們主要分析其中一個方法便可,讓咱們來看一下onItemRangeChanged()這個方法;
這裏面主要分爲兩步:
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { RecyclerView.this.assertNotInLayoutOrScroll((String)null); //這裏經過AdapterHelper將傳進來的信息保存起來 if (RecyclerView.this.mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) { //從新佈局 this.triggerUpdateProcessor(); } }
(1)經過AdapterHelper將傳進來的信息保存起來;
boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) { if (itemCount < 1) { return false; } else { this.mPendingUpdates.add(this.obtainUpdateOp(4, positionStart, itemCount, payload)); this.mExistingUpdateTypes |= 4; return this.mPendingUpdates.size() == 1; } }
(2)經過triggerUpdateProcessor()方法觸發RecyclerView從新佈局;
void triggerUpdateProcessor() { if (RecyclerView.POST_UPDATES_ON_ANIMATION && RecyclerView.this.mHasFixedSize && RecyclerView.this.mIsAttached) { //當前有動畫正在執行的時候會走這裏 ViewCompat.postOnAnimation(RecyclerView.this, RecyclerView.this.mUpdateChildViewsRunnable); } else { //觸發從新佈局 RecyclerView.this.mAdapterUpdateDuringMeasure = true; RecyclerView.this.requestLayout(); } }
首先咱們來看一下各個模塊的關係;
經過上圖大致能夠看出這幾個模塊的關係:
(1)RecyclerView經過LayoutManager來進行佈局操做;
(2)LayoutManager從Recycler裏面獲取複用的item來進行佈局;
(3)Recycler管理着ViewHolder的建立與複用;
(4)Adapter將數據和ViewHolder綁定起來,並和RecyclerView註冊觀察者;
(5)RecyclerView經過ItemDecoration進行item樣式的繪製;
接下來經過源碼來細細剖析,看看具體是怎麼實現的;
那麼咱們接着上面分析的setAdapter()方法繼續分析,在setAdapter()方法裏,最後調用來requestLayout(),來觸發RecyclerView 的繪製流程;
這個requestLayout()這個方法最終會調用到ViewRootImp裏面的requestLayout()方法;
public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; //觸發繪製流程 scheduleTraversals(); } }
在ViewRootImp裏調用requestLayout()方法進行繪製,咱們主要看scheduleTraversals()方法,裏面最終會調用到performTraversals()方法,源碼以下;
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } ... final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); ... void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } } ...
performTraversals()這個方法裏面執行了三大步驟,測量(measure),佈局(layout),繪製(draw),完成的view的工做流程,將頁面繪製出來;
{ // cache mView since it is used so much below... final View host = mView; ... if (!mStopped || mReportNextDraw) { //執行view的測量流程 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); } else { ... } ... if (didLayout) { //執行view的佈局流程 performLayout(lp, mWidth, mHeight); ... } ... if (!cancelDraw && !newSurface) { ... //執行view的繪製流程 performDraw(); } else { ... } }
從上面整理的方法來看,繪製流程主要是這performMeasure(),performLayout(),performDraw();最終會觸發RecyclerView的onMeasure(),onLayout(),onDraw()方法,具體源碼這裏就不過多分析了,感興趣的能夠看一下View的繪製流程;
讓咱們一個個來進行分析,先看看RecyclerView的onMeasure()方法裏面作了什麼?
protected void onMeasure(int widthSpec, int heightSpec) { if (mLayout == null) { //1.判斷當前的LayoutManger是否爲空,爲空則走RecyclerView默認測量的方法 ; defaultOnMeasure(widthSpec, heightSpec); return; } //2.LayoutManger開啓自動測量時走這裏處理邏輯; if (mLayout.mAutoMeasure) { final int widthMode = MeasureSpec.getMode(widthSpec); final int heightMode = MeasureSpec.getMode(heightSpec); final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY; mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); if (skipMeasure || 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 { //3.LayoutManger沒有開啓自動測量時走這裏處理邏輯; if (mHasFixedSize) { mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); return; } // custom onMeasure if (mAdapterUpdateDuringMeasure) { eatRequestLayout(); 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; resumeRequestLayout(false); } if (mAdapter != null) { mState.mItemCount = mAdapter.getItemCount(); } else { mState.mItemCount = 0; } eatRequestLayout(); mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); resumeRequestLayout(false); mState.mInPreLayout = false; // clear } }
這裏面主要分三種狀況,而咱們大部分狀況都是走第三步,經過查看官方的LayoutManger的源碼得知,LinearLayoutManager和StaggeredGridLayoutManager都開啓了自動測試,而GridLayoutManager繼承自LinearLayoutManager;因此,官方的LayoutManager都開啓了自動測量,這裏咱們只須要關注第二步的邏輯;從上面源碼能夠看出,RecyclerView經過LayoutManger裏的onMeasure()來進行測量操做;
經過State這個類來進行佈局和測試狀態的記錄,這裏的mLayoutStep 包括STEP_START、STEP_LAYOUT 、 STEP_ANIMATIONS三個狀態;從源碼分析,此時測量完畢以後,判斷當前狀態爲開始的時候(STEP_START),調用了dispatchLayoutStep1()進行了一系列的操做,這個方法執行完了以後,會將mLayoutStep 賦值爲STEP_LAYOUT;後面就執行了dispatchLayoutStep2(),在這個方法裏將mLayoutStep 賦值爲STEP_ANIMATIONS;
這裏咱們能夠理解爲,RecyclerView在測量完畢以後,就開始進行佈局了,分別執行了dispatchLayoutStep1()和dispatchLayoutStep2()方法;到此onMeasure()分析完了;
讓咱們繼續接着往下看,此時RecyclerView的onMeasure()已經執行完了,接下來會執行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; }
主要看dispatchLayout()這個方法
void dispatchLayout() { if (mAdapter == null) { Log.e(TAG, "No adapter attached; skipping layout"); // leave the state in START return; } if (mLayout == null) { Log.e(TAG, "No layout manager attached; skipping layout"); // leave the state in START return; } mState.mIsMeasuring = false; if (mState.mLayoutStep == State.STEP_START) { dispatchLayoutStep1(); mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() || mLayout.getHeight() != getHeight()) { // First 2 steps are done in onMeasure but looks like we have to run again due to // changed size. mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } else { // always make sure we sync them (to ensure mode is exact) mLayout.setExactMeasureSpecsFrom(this); } dispatchLayoutStep3(); }
經過上面源碼能夠看出,以前在onMeasure()裏的這個dispatchLayoutStep2()方法裏面已經把mLayoutStep 賦值爲STEP_ANIMATIONS,那麼這裏就會走最後一個方法dispatchLayoutStep3();若是沒有執行STEP_START方法,那麼就會依次執行dispatchLayoutStep1(),dispatchLayoutStep2(),dispatchLayoutStep3()這幾個佈局方法;讓咱們來一個個分析;
private void dispatchLayoutStep1() { mState.assertLayoutStep(State.STEP_START); mState.mIsMeasuring = false; eatRequestLayout(); mViewInfoStore.clear(); onEnterLayoutOrScroll(); processAdapterUpdatesAndSetAnimationFlags(); saveFocusInfo(); mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged; mItemsAddedOrRemoved = mItemsChanged = false; mState.mInPreLayout = mState.mRunPredictiveAnimations; mState.mItemCount = mAdapter.getItemCount(); findMinMaxChildLayoutPositions(mMinMaxLayoutPositions); if (mState.mRunSimpleAnimations) { // Step 0: Find out where all non-removed items are, pre-layout int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) { continue; } final ItemHolderInfo animationInfo = mItemAnimator .recordPreLayoutInformation(mState, holder, ItemAnimator.buildAdapterChangeFlagsForAnimations(holder), holder.getUnmodifiedPayloads()); mViewInfoStore.addToPreLayout(holder, animationInfo); if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved() && !holder.shouldIgnore() && !holder.isInvalid()) { long key = getChangedHolderKey(holder); // This is NOT the only place where a ViewHolder is added to old change holders // list. There is another case where: // * A VH is currently hidden but not deleted // * The hidden item is changed in the adapter // * Layout manager decides to layout the item in the pre-Layout pass (step1) // When this case is detected, RV will un-hide that view and add to the old // change holders list. mViewInfoStore.addToOldChangeHolders(key, holder); } } } if (mState.mRunPredictiveAnimations) { // Step 1: run prelayout: This will use the old positions of items. The layout manager // is expected to layout everything, even removed items (though not to add removed // items back to the container). This gives the pre-layout position of APPEARING views // which come into existence as part of the real layout. // Save old positions so that LayoutManager can run its mapping logic. saveOldPositions(); final boolean didStructureChange = mState.mStructureChanged; mState.mStructureChanged = false; // temporarily disable flag because we are asking for previous layout mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = didStructureChange; for (int i = 0; i < mChildHelper.getChildCount(); ++i) { final View child = mChildHelper.getChildAt(i); final ViewHolder viewHolder = getChildViewHolderInt(child); if (viewHolder.shouldIgnore()) { continue; } if (!mViewInfoStore.isInPreLayout(viewHolder)) { int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder); boolean wasHidden = viewHolder .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST); if (!wasHidden) { flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT; } final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation( mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads()); if (wasHidden) { recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo); } else { mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo); } } } // we don't process disappearing list because they may re-appear in post layout pass. clearOldPositions(); } else { clearOldPositions(); } onExitLayoutOrScroll(); resumeRequestLayout(false); mState.mLayoutStep = State.STEP_LAYOUT; }
這個方法主要作了ViewHolder信息的保存,裏面經過遍歷當前的子View,根據子view的位置信息建立ItemHolderInfo,並添加到 ViewInfoStore這個類裏面進行保存;
看一下ItemHolderInfo這個類;
public static class ItemHolderInfo { public int left; public int top; public int right; public int bottom; public ItemHolderInfo() { } ... public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder, @AdapterChanges int flags) { final View view = holder.itemView; this.left = view.getLeft(); this.top = view.getTop(); this.right = view.getRight(); this.bottom = view.getBottom(); return this; } } class ViewInfoStore { private static final boolean DEBUG = false; /** * View data records for pre-layout */ @VisibleForTesting final ArrayMap<RecyclerView.ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>(); @VisibleForTesting final LongSparseArray<RecyclerView.ViewHolder> mOldChangedHolders = new LongSparseArray<>(); /** * Clears the state and all existing tracking data */ void clear() { mLayoutHolderMap.clear(); mOldChangedHolders.clear(); } /** * Adds the item information to the prelayout tracking * @param holder The ViewHolder whose information is being saved * @param info The information to save */ void addToPreLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) { InfoRecord record = mLayoutHolderMap.get(holder); if (record == null) { record = InfoRecord.obtain(); mLayoutHolderMap.put(holder, record); } record.preInfo = info; record.flags |= FLAG_PRE; } }
經過源碼能夠看出,在dispatchLayoutStep1()方法裏會先遍歷子view,並建立ItemHolderInfo,而後再經過ViewInfoStore的addToPreLayout()的這個方法將ItemHolderInfo賦值給InfoRecord,再保存到mLayoutHolderMap這個集合裏面;
下面咱們再來分析一下dispatchLayoutStep2()這個方法裏面作來啥?
private void dispatchLayoutStep2() { private void dispatchLayoutStep2() { startInterceptRequestLayout(); onEnterLayoutOrScroll(); mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS); mAdapterHelper.consumeUpdatesInOnePass(); mState.mItemCount = mAdapter.getItemCount(); mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; // Step 2: Run layout mState.mInPreLayout = false; // 開始真正的去佈局 mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = false; mPendingSavedState = null; // onLayoutChildren may have caused client code to disable item animations; re-check mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null; mState.mLayoutStep = State.STEP_ANIMATIONS; onExitLayoutOrScroll(); stopInterceptRequestLayout(false); } }
經過上面的源碼能夠看出,dispatchLayoutStep2()裏面就開始真正的去佈局了,經過onLayoutChildre()方法進行佈局,具體的實現都在LayoutManager的子類裏面;咱們經常使用的LayoutManager基本上是LinearLayoutManager,那麼這裏咱們具體來分析一下這個類裏面是怎麼實現的;
先看一下源碼:
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { ... final View focused = getFocusedChild(); if (!mAnchorInfo.mValid || mPendingScrollPosition != RecyclerView.NO_POSITION || mPendingSavedState != null) { mAnchorInfo.reset(); // 獲取佈局的錨點 updateAnchorInfoForLayout(recycler, state, mAnchorInfo); mAnchorInfo.mValid = true; } else if (focused != null && (mOrientationHelper.getDecoratedStart(focused) >= mOrientationHelper.getEndAfterPadding() || mOrientationHelper.getDecoratedEnd(focused) <= mOrientationHelper.getStartAfterPadding())) { ... // 更新錨點信息 mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused)); } //判斷是不是從後往前開始佈局 if (mAnchorInfo.mLayoutFromEnd) { ... //佈局操做 fill(recycler, mLayoutState, state, false); ... } else { ... // fill towards end fill(recycler, mLayoutState, state, false); // fill towards start fill(recycler, mLayoutState, state, false); ... } ... }
這裏把代碼簡化了,咱們只須要關注幾個重點的方法;這裏的佈局操做是,經過尋找佈局的錨點(mAnchorInfo),判斷是從後往前佈局仍是從前日後佈局,而後調用fill()方法進行佈局;
尋找佈局的錨點是經過updateAnchorInfoForLayout(recycler, state, mAnchorInfo)這個方法
private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state, AnchorInfo anchorInfo) { ... if (updateAnchorFromChildren(recycler, state, anchorInfo)) { if (DEBUG) { Log.d(TAG, "updated anchor info from existing children"); } return; } ... }
這裏咱們只須要關注updateAnchorFromChildren這個方法,跟進去看一下具體作了什麼;
private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler, RecyclerView.State state, AnchorInfo anchorInfo) { if (getChildCount() == 0) { return false; } final View focused = getFocusedChild(); if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) { anchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused)); return true; } if (mLastStackFromEnd != mStackFromEnd) { return false; } View referenceChild = anchorInfo.mLayoutFromEnd ? findReferenceChildClosestToEnd(recycler, state) : findReferenceChildClosestToStart(recycler, state); if (referenceChild != null) { anchorInfo.assignFromView(referenceChild, getPosition(referenceChild)); ... } return true; } return false; }
從這裏的源碼能夠看出,先經過getFocusedChild()去獲取focused 這個view,當獲取到了的時候將其標記爲錨點,若是獲取不到那麼就經過findReferenceChildClosestToEnd和findReferenceChildClosestToStart去尋找合適的view,並將其標記爲錨點;
讓咱們回到onLayoutChildren這個方法,當獲取到錨點的時候,調用fill方法開始填充頁面,根據fill方法看看具體作了什麼?
int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) { ... if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { //回收沒有用到的view recycleByLayoutState(recycler, layoutState); } ... while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { layoutChunk(recycler, state, layoutState, layoutChunkResult); ... } }
這裏經過recycleByLayoutState方法先將沒有用到view進行回收,而後再經過while循環調用layoutChunk方法進行佈局;
看一下layoutChunk方法具體作了什麼操做?
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { View view = layoutState.next(recycler); ... RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); if (layoutState.mScrapList == null) { if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) { addView(view); } else { addView(view, 0); } } else { ... } ... layoutDecoratedWithMargins(view, left, top, right, bottom); ... }
到這裏就是最終佈局的地方了,先經過recycler獲取要佈局的view,再經過addView方法將view添加到RecyclerView裏去,而後根據參數調用layoutDecoratedWithMargins方法進行佈局;
public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right, int bottom) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final Rect insets = lp.mDecorInsets; child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin, right - insets.right - lp.rightMargin, bottom - insets.bottom - lp.bottomMargin); }
這裏最終調用了view的layout方法進行佈局;到這裏dispatchLayoutStep2()就分析完了,讓咱們繼續接着看dispatchLayoutStep3()第三步裏面作了啥;
private void dispatchLayoutStep3() { mState.assertLayoutStep(State.STEP_ANIMATIONS); ... mState.mLayoutStep = State.STEP_START; if (mState.mRunSimpleAnimations) { ... for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) { ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore()) { continue; } long key = getChangedHolderKey(holder); final ItemHolderInfo animationInfo = mItemAnimator .recordPostLayoutInformation(mState, holder); ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key); ... if (oldDisappearing && oldChangeViewHolder == holder) { // run disappear animation instead of change mViewInfoStore.addToPostLayout(holder, animationInfo); } else { ... mViewInfoStore.addToPostLayout(holder, animationInfo); ... } } else { mViewInfoStore.addToPostLayout(holder, animationInfo); } } // Step 4: Process view info lists and trigger animations //觸發動畫 mViewInfoStore.process(mViewInfoProcessCallback); } ... }
這個方法裏面只須要關注addToPostLayout這個方法就行,這裏和第一步相似,也是經過遍歷viewholder信息來建立ItemHolderInfo,並保存到mViewInfoStore裏去;
看一下addToPostLayout這個方法作了啥?
void addToPostLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) { InfoRecord record = mLayoutHolderMap.get(holder); if (record == null) { record = InfoRecord.obtain(); mLayoutHolderMap.put(holder, record); } record.postInfo = info;
也是經過將ItemHolderInfo信息轉化爲InfoRecord類,而後保存到集合裏去(mLayoutHolderMap);
到此,RecyclerView的onLayout流程就已經走完了;那麼接下來就要開始分析onDraw的流程了;
先看一下源碼;
public void draw(Canvas c) { super.draw(c); ... for (int i = 0; i < count; i++) { mItemDecorations.get(i).onDrawOver(c, this, mState); } ... } 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); } }
很簡單,就幾行,mItemDecorations這個集合裏面存的是ItemDecoration,也就是說,RecyclerView的onDraw是用來繪製ItemDecoration的;而itemView的繪製是在ViewGroup裏面;
至此,RecyclerView的onMeasure,onLayout,onDraw,流程就已經分析完畢了;
RecyclerView的佈局流程比較複雜,可是仍是遵循viewGroup的繪製原理,即onMeasure,onLayout,onDraw這幾步流程;
那麼到這裏,繪製的流程就已經講完了,但願能對你有所幫助,後面會繼續分析RecyclerView的複用機制,敬請期待!