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