前言: 当前市场上有不少成熟的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的复用机制,敬请期待!