View的绘制
onMeasure
开始,如果mLayout是空的,就会调用默认绘制方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
if (mLayout == null) { defaultOnMeasure(widthSpec, heightSpec); return; } ** 默认的绘制方法 void defaultOnMeasure(int widthSpec, int heightSpec) { // 为RecyclerView测量最小大小 final int width = LayoutManager.chooseSize(widthSpec, getPaddingLeft() + getPaddingRight(), ViewCompat.getMinimumWidth(this)); final int height = LayoutManager.chooseSize(heightSpec, getPaddingTop() + getPaddingBottom(), ViewCompat.getMinimumHeight(this)); setMeasuredDimension(width, height); } |
如果mLayout是自动测量,让mLayout进行测量,然后根据mState.mLayoutStep的状态进行是否 dispatchLayoutStep1(),接着dispatchLayoutStep2()等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
if (mLayout.mAutoMeasure) { ... // 让LayoutManager负责测量 ,默认的方法是defaultOnMeasure,也可自己重写 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); if (skipMeasure || mAdapter == null) { return; } // mState.mLayoutStep 是负责对应几个状态,一般状态STEP_START,需要dispatchLayoutStep1() if (mState.mLayoutStep == State.STEP_START) { dispatchLayoutStep1(); } // 值得注意的是,setMeasureSpecs这个方法是放在dispatchLayoutStep1()后面,然后才开始真正测量大小 mLayout.setMeasureSpecs(widthSpec, heightSpec); // 测量完毕,修改状态为true mState.mIsMeasuring = true; dispatchLayoutStep2(); // 测量子类大小, mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec); // 如果需要进行两次测量 if (mLayout.shouldMeasureTwice()) { 继续重新测量自己大小和子类大小 ... } } else { ... |
如果不是自动测量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 这里注意 如果设置了hasFixedSize,直接mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec),然后返回 // 就不在需要考虑adapter个别item的改变,onItemRangeChanged if (mHasFixedSize) { mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); return; } // 如果是执行了onItemRangeChanged,onItemRangeInserted等针对一个item的监听会触发 // 主要是针对adapter if (mAdapterUpdateDuringMeasure) { eatRequestLayout(); onEnterLayoutOrScroll(); processAdapterUpdatesAndSetAnimationFlags(); onExitLayoutOrScroll(); ... mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); resumeRequestLayout(false); mState.mInPreLayout = false; // clear } |
大致分析整个Measure流程,最主要的还是mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);还是交给了mLayout进行测量
onLayout
onLayout里主要是分为了三个步骤,分别是dispatchLayoutStep1(),dispatchLayoutStep2(),dispatchLayoutStep3();
mState.mLayoutStep有三种状态,分别是:STEP_START,STEP_LAYOUT,STEP_ANIMATIONS
mLayout.setExactMeasureSpecsFrom(this); 这个是都执行了,作用是把RecyclerView的宽,高都设置为了MeasureSpec.EXACTLY模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
mState.mIsMeasuring = false; if (mState.mLayoutStep == State.STEP_START) { // 如果还是STEP_START,那就先dispatchLayoutStep1() dispatchLayoutStep1(); mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() || mLayout.getHeight() != getHeight()) { // 已经重新有测量过 mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } else { mLayout.setExactMeasureSpecsFrom(this); } dispatchLayoutStep3(); |
dispatchLayoutStep1()
里面十分复杂,根据注释结合理解,主要作用是对Adapter进行更新,计算保存item的动画信息,绝对哪个动画要执行,并且不断调整mState的信息,
里面有个重要的方法,processAdapterUpdatesAndSetAnimationFlags,最主要目的是处理item动画
processAdapterUpdatesAndSetAnimationFlags()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
private void processAdapterUpdatesAndSetAnimationFlags() { if (mDataSetHasChangedAfterLayout) { // 是否有重新设置过数据,比如notofydatasetCHange // 所以我们要重新reset, // AdapterHelper.reset 主要是 记录UpdateOp的list全部reset掉 // UpdateOp 是来处理跟踪item相关操作的 mAdapterHelper.reset(); mLayout.onItemsChanged(this); } //是否支持预期动画 if (predictiveItemAnimationsEnabled()) { mAdapterHelper.preProcess(); } else { mAdapterHelper.consumeUpdatesInOnePass(); } //mRunSimpleAnimations 为true //1.mFirstLayoutComplete为true(onLayout第一次执行完后被置为true) //2.mItemAnimator不为空 //3.Layout后数据发生了变化 或 有item被移除或添加 或 LayoutManager请求执行simple animations //4.Layout后数据不发生变化 或 mAdapter有稳定的ID boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged; mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null && (mDataSetHasChangedAfterLayout || animationTypeSupported || mLayout.mRequestedSimpleAnimations) && (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds()); //mRunPredictiveAnimations 为true // 1.mRunSimpleAnimations为true // 2.有item添加或移除 // 3.Layout后数据未发生变化 // 4.预期Item动画被开启 mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations && animationTypeSupported && !mDataSetHasChangedAfterLayout && predictiveItemAnimationsEnabled(); } |
dispatchLayoutStep1() 大致就是帮助我们处理adapter更新,确定执行动画,保存view的信息等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
private void dispatchLayoutStep1() { 清除mViewInfoStore的信息,如果还在滑动,记录滑动的信息到mState等等 ... // 这个方法比较重要 processAdapterUpdatesAndSetAnimationFlags(); ... // 确定第一个和最后一个view的位置信息 findMinMaxChildLayoutPositions(mMinMaxLayoutPositions); // 如果要执行simple动画就 if (mState.mRunSimpleAnimations) { .... 获取对应的信息并且添加到mViewInfoStore中 final ItemHolderInfo animationInfo = mItemAnimator .recordPreLayoutInformation(mState, holder, ItemAnimator.buildAdapterChangeFlagsForAnimations(holder), holder.getUnmodifiedPayloads()); mViewInfoStore.addToPreLayout(holder, animationInfo); ... mViewInfoStore.addToOldChangeHolders(key, holder); } } } if (mState.mRunPredictiveAnimations) { ... 同上,如果要 // 这里会再次调用onLayoutChildren方法 mLayout.onLayoutChildren(mRecycler, mState); } // 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; } |
最后把mState的状态改为STEP_LAYOUT,可以说是绘制Layout前的准备
### dispatchLayoutStep2
该方法开始 mLayout.onLayoutChildren(mRecycler, mState); 方法进行布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
private void dispatchLayoutStep2() { // 进入布局 eatRequestLayout(); onEnterLayoutOrScroll(); mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS); // 跳过预期动画的处理 mAdapterHelper.consumeUpdatesInOnePass(); mState.mItemCount = mAdapter.getItemCount(); mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; // 开始调用 mLayout.onLayoutChildren(mRecycler, mState); 方法进行布局 mState.mInPreLayout = false; mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = false; mPendingSavedState = null; //最后把状态修改为State.STEP_ANIMATIONS; mState.mLayoutStep = State.STEP_ANIMATIONS; onExitLayoutOrScroll(); resumeRequestLayout(false); } |
### dispatchLayoutStep3
这方法主要是进行动画的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
private void dispatchLayoutStep3() { // 进入布局阶段,并且修改为State.STEP_START mState.assertLayoutStep(State.STEP_ANIMATIONS); eatRequestLayout(); onEnterLayoutOrScroll(); mState.mLayoutStep = State.STEP_START; if (mState.mRunSimpleAnimations) { // 开始执行每个view的信息检查,是否要处理动画 for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) { ... 找到ItemHolderInfo 保存了view的信息 final ItemHolderInfo animationInfo = mItemAnimator .recordPostLayoutInformation(mState, holder); // 旧的view的信息 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key); if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) { final boolean oldDisappearing = mViewInfoStore.isDisappearing( oldChangeViewHolder); final boolean newDisappearing = mViewInfoStore.isDisappearing(holder); // 如果之前的view信息是消失了,不需要进行动画处理 if (oldDisappearing && oldChangeViewHolder == holder) { mViewInfoStore.addToPostLayout(holder, animationInfo); } else { // 否则得到之前的ItemHolderInfo final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout( oldChangeViewHolder); mViewInfoStore.addToPostLayout(holder, animationInfo); ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder); if (preInfo == null) { // 如果之前的preInfo为空 handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder); } else { // 不为空的话,动画处理 animateChange(oldChangeViewHolder, holder, preInfo, postInfo, oldDisappearing, newDisappearing); } } ... } mViewInfoStore.process(mViewInfoProcessCallback); } 一系列收尾工作 ... |
简单分析了解了onLayout的流程,可以看出最主要的布局还是交给了mLayout去执行,当然里面还有有细节需要另外分析。
onDraw
主要还是我们平时一些分割线的绘制回调
draw
主要还是调用了mItemDecorations中的onDrawOver方法
1 2 3 4 5 6 7 8 9 |
final int count = mItemDecorations.size(); for (int i = 0; i < count; i++) { // 调用了mItemDecorations中的onDrawOver方法 mItemDecorations.get(i).onDrawOver(c, this, mState); } ... 中间部分是 滑动到左边右边上边下边时候的EdgeEffect if (needsInvalidate) { ViewCompat.postInvalidateOnAnimation(this); } |
onDraw
主要还是调用了mItemDecorations中的onDraw方法 @Override public void onDraw(Canvas c) { super.onDraw©;
1 2 3 4 5 |
final int count = mItemDecorations.size(); for (int i = 0; i < count; i++) { mItemDecorations.get(i).onDraw(c, this, mState); } } |
LinearLayoutManager
从之前的onLayout三步骤去分析:
dispatchLayoutStep1
如果是允许预期动画了,此时会调用mLayout.onLayoutChildren(mRecycler, mState);
然后dispatchLayoutStep1完后 会调用mLayout.setExactMeasureSpecsFrom(this); 确定大小
1 2 3 4 5 6 7 |
... if (mState.mRunPredictiveAnimations) { ... mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = didStructureChange; ... } |
dispatchLayoutStep2
1 2 3 4 5 6 7 |
private void dispatchLayoutStep2() { ... // Step 2: Run layout mState.mInPreLayout = false; mLayout.onLayoutChildren(mRecycler, mState); ... } |
dispatchLayoutStep3
步骤3的时候 removeAndRecycleScrapInt,最后onLayoutCompleted
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private void dispatchLayoutStep3() { ... mLayout.removeAndRecycleScrapInt(mRecycler); ... mLayout.mRequestedSimpleAnimations = false; ... //初始预取扩展了缓存,因此重置到下一个预取。 //这防止初始预取永久地扩展高速缓存。 if (mLayout.mPrefetchMaxObservedInInitialPrefetch) { mLayout.mPrefetchMaxCountObserved = 0; mLayout.mPrefetchMaxObservedInInitialPrefetch = false; mRecycler.updateViewCacheSize(); } mLayout.onLayoutCompleted(mState); ... |
onLayoutChildren(mRecycler, mState)
核心方法,计算mAnchorInfo,判断mAnchorInfo.mLayoutFromEnd方向,执行对应的fling方法,最后还要重新检查更新那些位置有差距,进行修补
关于fling方法,调用layoutChunk方法,里面view的添加,位置摆放全放在里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
@Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { ... 重置mAnchorInfo的属性,并且重新设置mAnchorInfo // detachAndScrapAttachedViews方法回调了recyclerview中的scrapOrRecycleView方法 detachAndScrapAttachedViews(recycler); mLayoutState.mInfinite = resolveIsInfinite(); mLayoutState.mIsPreLayout = state.isPreLayout(); if (mAnchorInfo.mLayoutFromEnd) { // 如果方向是start ... } else {// 如果方向是end,看一个就好 // 更新mAnchorInfo状态 // 分别想end方向,start方向,执行fling方法 // 最后如果不够,mAvailable>0 我们还需要填充 updateLayoutStateToFillEnd(mAnchorInfo); mLayoutState.mExtra = extraForEnd; fill(recycler, mLayoutState, state, false); endOffset = mLayoutState.mOffset; final int lastElement = mLayoutState.mCurrentPosition; if (mLayoutState.mAvailable > 0) { extraForStart += mLayoutState.mAvailable; } // fill towards start updateLayoutStateToFillStart(mAnchorInfo); mLayoutState.mExtra = extraForStart; mLayoutState.mCurrentPosition += mLayoutState.mItemDirection; fill(recycler, mLayoutState, state, false); startOffset = mLayoutState.mOffset; if (mLayoutState.mAvailable > 0) { extraForEnd = mLayoutState.mAvailable; // start could not consume all it should. add more items towards end updateLayoutStateToFillEnd(lastElement, endOffset); mLayoutState.mExtra = extraForEnd; fill(recycler, mLayoutState, state, false); endOffset = mLayoutState.mOffset; } } // 最后还要重新检查更新那些位置有差距,进行修补 if (getChildCount() > 0) { // because layout from end may be changed by scroll to position // we re-calculate it. // find which side we should check for gaps. if (mShouldReverseLayout ^ mStackFromEnd) { int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true); startOffset += fixOffset; endOffset += fixOffset; fixOffset = fixLayoutStartGap(startOffset, recycler, state, false); startOffset += fixOffset; endOffset += fixOffset; } else { int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true); startOffset += fixOffset; endOffset += fixOffset; fixOffset = fixLayoutEndGap(endOffset, recycler, state, false); startOffset += fixOffset; endOffset += fixOffset; } } // 执行了预期动画 layoutForPredictiveAnimations(recycler, state, startOffset, endOffset); ... } |
这里不详细去跟,至于findLastCompletelyVisibleItemPosition等方法是 根据ViewBoundsCheck类中的findOneViewWithinBoundFlags方法去匹配,初始化是在RecyclerVIew上
## Recycler
核心类,整个回收机制都在这个类上
### recycleView
首先是调用scrapOrRecycleView进行mAttachedScrap或者是mChangedScrap的缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private void scrapOrRecycleView(Recycler recycler, int index, View view) { ... 判断可以看到如果是viewHolder初始化没有真正移除,mAdapter没有hasStableIds() 内部缓存recycleViewHolderInternal(viewHolder) 否则 scrapView 缓存 detachViewAt 然后回收该view if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !mRecyclerView.mAdapter.hasStableIds()) { removeViewAt(index); recycler.recycleViewHolderInternal(viewHolder); } else { detachViewAt(index); recycler.scrapView(view); mRecyclerView.mViewInfoStore.onViewDetached(viewHolder); } } |
scrapView
该方法也是通过一系列的判断是 mAttachedScrap.add(holder); 还是 mChangedScrap.add(holder);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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); } } |
然后cache缓存一个viwe的时候可以调用 public void recycleView(View view) 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void recycleView(View view) { // This public recycle method tries to make view recycle-able since layout manager // intended to recycle this view (e.g. even if it is in scrap or change cache) ViewHolder holder = getChildViewHolderInt(view); if (holder.isTmpDetached()) { removeDetachedView(view, false); } if (holder.isScrap()) { holder.unScrap(); } else if (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } |
关键是调用了recycleViewHolderInternal(holder);
### recycleViewHolderInternal
该方法是recycler如何缓存viewholder的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
void recycleViewHolderInternal(ViewHolder holder) { 开始对holder进行一些判断,如果不满足 抛出异常 ... if (forceRecycle || holder.isRecyclable()) { // 如果强行recycle或者允许recycyle // 如果mViewCacheMax大于0 if (mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) { // 如果缓存的view的数量大于了最大值,就把第一个给移除,添加到pool中 int cachedViewSize = mCachedViews.size(); if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) { recycleCachedViewAt(0); cachedViewSize--; } int targetCacheIndex = cachedViewSize; ... 这里会找到targetCacheIndex targetCacheIndex = cacheIndex + 1; ... 最终添加到cacheview中 mCachedViews.add(targetCacheIndex, holder); cached = true; 说明已经缓存过 } if (!cached) { 没有缓存过 就加到 addViewHolderToRecycledViewPool(holder, true); recycled = true; } } else { } } |
### recycleCachedViewAt
将cacheview的移除,放到pool那里去
1 2 3 4 |
... addViewHolderToRecycledViewPool(viewHolder, true); mCachedViews.remove(cachedViewIndex);、 ... |
### tryGetViewHolderForPositionByDeadline
获取ViewHolder的时候会调用tryGetViewHolderForPositionByDeadline方法
getChangedScrapViewForPosition方法是获取mChangedScrap里面的holder
getScrapOrHiddenOrCachedHolderForPosition方法是获取mAttachedScrap里面的holder
RecyclerPool 通过mScrapHeap缓存,默认缓存5个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { ... 如果是mState的状态是准备layout,通过chagngeview获取holder if (mState.isPreLayout()) { // 从mChangedScrap里面的holder holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; } // holder依然是空 if (holder == null) { // 通过pos,mAttachedScrap里面的holder,没有继续根据mCachedViews中的 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); // 如果holder不是空 if (holder != null) { // 检测该holder是否可用 if (!validateViewHolderForOffsetPosition(holder)) { ... 可用缓存起来 ... } else { fromScrapOrHiddenOrCache = true; } } } // 如果holder还是空 if (holder == null) { ... if (mAdapter.hasStableIds()) { // 通过 stable id 先从mAttachedScrap ,然后再从mCachedViews holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun); ... if (holder == null && mViewCacheExtension != null) { final View view = mViewCacheExtension .getViewForPositionAndType(this, position, type); if (view != null) { // 从mViewCacheExtension获取view,如果view不为空,在从里面获取holder holder = getChildViewHolder(view); if (holder == null) { // 需要注意如果view不为空,holder空了 会报错 throw new IllegalArgumentException("getViewForPositionAndType returned" + " a view which does not have a ViewHolder" + exceptionLabel()); ... if (holder == null) { // fallback to pool ... 如果还是空,从pool中获取holder holder = getRecycledViewPool().getRecycledView(type); ... } if (holder == null) { ... 最后才调用createViewHolder方法去createHolder holder = mAdapter.createViewHolder(RecyclerView.this, type); .... return holder; } |
大致总结:首先是判断mState的状态,如果是isPreLayout()则从mChangeSrap根据pos获取,然后是1级缓存mAttachScrap 通过pos获取,mCacheView 通过pos获取,接着,根据adapter stable id 获取,然后mViewCacheExtension获取view中的holder,最后才从pool中获取,没有就createHolder