RecyclerView源码解析(-)-绘制流程
因为RecyclerView是继承ViewGroup的,所以这面从onMeasure(), draw(),onDraw(),onLayout()方法去看RecyclerView的绘制源码!
总体来说就是:在onMeasure()是否是自动测量
1,自动测量(现在几乎默认都是自动测量的)
(1),委托LayoutManager来测量RecyclerView的宽高(还是走defaultOnMeasure方法)拿到父view的spec进行设置尺寸,
(2),当前RecyclerView的宽高是否都为精确值,如果为精确值onMeasure直接reture,否则进行dispatchLayoutStep1(),dispatchLayoutStep2(),在通过addChildView之后重新计算RecyclerView的宽高!
但是这面的问题是return之后childView怎么添加呢,没事的因为在onLayout()方面里面会进行判断,如没addView会再一次的dispatchLayoutStep1(),dispatchLayoutStep2(),到dispatchLayoutStep3()。
(3),对于绘制,draw()和onDraw()分别绘制了ItemDecoration onDrawOver(),边界阴影和ItemDecoration onDraw()!
2,非自动测量
(1),就会完全靠layout绘制了
onMeasure()
protected void onMeasure(int widthSpec, int heightSpec) {
//如果mLayout为空,直接会走默认的父view传递的Spec
if (this.mLayout == null) {
this.defaultOnMeasure(widthSpec, heightSpec);
} else {
//是否能自动测量(像LinearLayoutManager和剩余两个其实返回的都是true)
if (!this.mLayout.isAutoMeasureEnabled()) {
//是否有不变的大小,如果有不变的大小就直接设置measure并reture掉!
if (this.mHasFixedSize) {
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
return;
}
//表明Adapter有数据变化的时候(changed,inserted,removed,moved)就会变为true
if (this.mAdapterUpdateDuringMeasure) {
this.startInterceptRequestLayout();
this.onEnterLayoutOrScroll();
this.processAdapterUpdatesAndSetAnimationFlags();
this.onExitLayoutOrScroll();
if (this.mState.mRunPredictiveAnimations) {
this.mState.mInPreLayout = true;
} else {
this.mAdapterHelper.consumeUpdatesInOnePass();
this.mState.mInPreLayout = false;
}
this.mAdapterUpdateDuringMeasure = false;
this.stopInterceptRequestLayout(false);
} else if (this.mState.mRunPredictiveAnimations) {
this.setMeasuredDimension(this.getMeasuredWidth(), this.getMeasuredHeight());
return;
}
if (this.mAdapter != null) {
this.mState.mItemCount = this.mAdapter.getItemCount();
} else {
this.mState.mItemCount = 0;
}
this.startInterceptRequestLayout();
//设置尺寸!
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
this.stopInterceptRequestLayout(false);
this.mState.mInPreLayout = false;
} else {
int widthMode = MeasureSpec.getMode(widthSpec);
int heightMode = MeasureSpec.getMode(heightSpec);
//委托LayoutManager来测量RecyclerView的宽高(还是走defaultOnMeasure方法)
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
//当前RecyclerView的宽高是否都为精确值
boolean measureSpecModeIsExactly = widthMode == 1073741824 && heightMode == 1073741824;
//如果RecyclerView的宽高都是写死的精确值或者是match_parent并且Adapter还没有设置就结束测量
//因为前面的 this.mLayout.onMeasure()这一方法已经走了测量,因为有精确的尺寸,所以直接就reture掉了!
if (measureSpecModeIsExactly || this.mAdapter == null) {
return;
}
//当宽高为wrap_content时,就先不能确定RecyclerView的宽高,因为需要先测量子itemView的宽高后才可以确定自己的宽高
if (this.mState.mLayoutStep == 1) {
//做一些view信息存储等的准备工作.
this.dispatchLayoutStep1();
}
this.mLayout.setMeasureSpecs(widthSpec, heightSpec);
this.mState.mIsMeasuring = true;
//借助LayoutManager为RecyclerView添加view;
this.dispatchLayoutStep2();
//通过添加childView之后再一次计算RecyclerView的宽高!这样就确定了RecyclerView的宽高!
this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
//是否要进行第二次测量,如果进行第二次测量则再一次重新dispatchLayoutStep2();
//setMeasuredDimensionFromChildren(widthSpec, heightSpec)
if (this.mLayout.shouldMeasureTwice()) {
this.mLayout.setMeasureSpecs(MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), 1073741824), MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), 1073741824));
this.mState.mIsMeasuring = true;
this.dispatchLayoutStep2();
this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
}
}
}
}
仔细分析下,如果mLayout等于空,而mLayout就是LayoutManager,所以这面应该不是空的,所以会走到下面的else里面,而这时又会面临一个判断语句,mLayout.isAutoMeasureEnabled(),因为像LinearLayoutManager, GridLayoutManager及StaggeredGridLayoutManager都重写了
isAutoMeasureEnabled()这一方法,所以这面返回的应该都是true,这样就会走到下面的else{}语句里面。
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
}
因为mLayoutStep 默认就是1,所以会走
private void dispatchLayoutStep1() {
this.mState.assertLayoutStep(1);
this.fillRemainingScrollValues(this.mState);
this.mState.mIsMeasuring = false;
this.startInterceptRequestLayout();
this.mViewInfoStore.clear();
this.onEnterLayoutOrScroll();
this.processAdapterUpdatesAndSetAnimationFlags();
this.saveFocusInfo();
this.mState.mTrackOldChangeHolders = this.mState.mRunSimpleAnimations && this.mItemsChanged;
this.mItemsAddedOrRemoved = this.mItemsChanged = false;
this.mState.mInPreLayout = this.mState.mRunPredictiveAnimations;
this.mState.mItemCount = this.mAdapter.getItemCount();
this.findMinMaxChildLayoutPositions(this.mMinMaxLayoutPositions);
int i;
//是否要执行动画
if (this.mState.mRunSimpleAnimations) {
//拿到当前屏幕展示的childView数量,
int count = this.mChildHelper.getChildCount();
for(i = 0; i < count; ++i) {
RecyclerView.ViewHolder holder = getChildViewHolderInt(this.mChildHelper.getChildAt(i));
if (!holder.shouldIgnore() && (!holder.isInvalid() || this.mAdapter.hasStableIds())) {
RecyclerView.ItemAnimator.ItemHolderInfo animationInfo = this.mItemAnimator.recordPreLayoutInformation(this.mState, holder, RecyclerView.ItemAnimator.buildAdapterChangeFlagsForAnimations(holder), holder.getUnmodifiedPayloads());
// 存储预布局信息,也就是当前的没有进行动画的childView信息
this.mViewInfoStore.addToPreLayout(holder, animationInfo);
if (this.mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved() && !holder.shouldIgnore() && !holder.isInvalid()) {
long key = this.getChangedHolderKey(holder);
this.mViewInfoStore.addToOldChangeHolders(key, holder);
}
}
}
}
//这面也是跟ItemAnim动画相关的操作,到时候itemAnimator会细说!大体上就是在this.mLayout.onLayoutChildren(this.mRecycler, this.mState)这个方法之后把不是preLayout的viewHolder加入集合中,
//等待执行动画!
if (this.mState.mRunPredictiveAnimations) {
this.saveOldPositions();
boolean didStructureChange = this.mState.mStructureChanged;
this.mState.mStructureChanged = false;
this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
this.mState.mStructureChanged = didStructureChange;
for(i = 0; i < this.mChildHelper.getChildCount(); ++i) {
View child = this.mChildHelper.getChildAt(i);
RecyclerView.ViewHolder viewHolder = getChildViewHolderInt(child);
if (!viewHolder.shouldIgnore() && !this.mViewInfoStore.isInPreLayout(viewHolder)) {
int flags = RecyclerView.ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
boolean wasHidden = viewHolder.hasAnyOfTheFlags(8192);
if (!wasHidden) {
flags |= 4096;
}
RecyclerView.ItemAnimator.ItemHolderInfo animationInfo = this.mItemAnimator.recordPreLayoutInformation(this.mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
if (wasHidden) {
this.recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
} else {
this.mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
}
}
}
this.clearOldPositions();
} else {
this.clearOldPositions();
}
this.onExitLayoutOrScroll();
this.stopInterceptRequestLayout(false);
this.mState.mLayoutStep = 2;
}
当measure的时候!一开始进行measure()的时候 this.mState.mRunSimpleAnimations 和 this.mState.mRunPredictiveAnimations肯定是false的,其实dispatchLayoutStep1()这个方法就是RecyclerView的pre layout阶段,用于记录数据集改变前子控件的信息(存储了一些信息到mViewInfoStore中)。
所以第一次onLayout的时候,肯定不会执行预布局。所以preLayout并不是在第一次measure和layout前所存在的状态,而是在数据集发生变化的时候,所应该具有的状态!
在回到所谓的onMeasure()方法中,继续往下走,会走到dispatchLayoutStep2();
private void dispatchLayoutStep2() {
this.startInterceptRequestLayout();
this.onEnterLayoutOrScroll();
this.mState.assertLayoutStep(6);
this.mAdapterHelper.consumeUpdatesInOnePass();
this.mState.mItemCount = this.mAdapter.getItemCount();
this.mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
this.mState.mInPreLayout = false;
this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
this.mState.mStructureChanged = false;
this.mPendingSavedState = null;
this.mState.mRunSimpleAnimations = this.mState.mRunSimpleAnimations && this.mItemAnimator != null;
this.mState.mLayoutStep = 4;
this.onExitLayoutOrScroll();
this.stopInterceptRequestLayout(false);
}
这面的方法主要还是
this.mState.mInPreLayout = false;
this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
这面的onLayoutChildren(this.mRecycler, this.mState) 走的就是LayouManager的onLayoutChildren()方法!
进行视图的摆放与填充!
draw()
public void draw(Canvas c) {
super.draw(c);
int count = this.mItemDecorations.size();
for(int i = 0; i < count; ++i) {
((RecyclerView.ItemDecoration)this.mItemDecorations.get(i)).onDrawOver(c, this, this.mState);
}
boolean needsInvalidate = false;
int restore;
int width;
if (this.mLeftGlow != null && !this.mLeftGlow.isFinished()) {
restore = c.save();
width = this.mClipToPadding ? this.getPaddingBottom() : 0;
c.rotate(270.0F);
c.translate((float)(-this.getHeight() + width), 0.0F);
needsInvalidate = this.mLeftGlow != null && this.mLeftGlow.draw(c);
c.restoreToCount(restore);
}
if (this.mTopGlow != null && !this.mTopGlow.isFinished()) {
restore = c.save();
if (this.mClipToPadding) {
c.translate((float)this.getPaddingLeft(), (float)this.getPaddingTop());
}
needsInvalidate |= this.mTopGlow != null && this.mTopGlow.draw(c);
c.restoreToCount(restore);
}
if (this.mRightGlow != null && !this.mRightGlow.isFinished()) {
restore = c.save();
width = this.getWidth();
int padding = this.mClipToPadding ? this.getPaddingTop() : 0;
c.rotate(90.0F);
c.translate((float)(-padding), (float)(-width));
needsInvalidate |= this.mRightGlow != null && this.mRightGlow.draw(c);
c.restoreToCount(restore);
}
if (this.mBottomGlow != null && !this.mBottomGlow.isFinished()) {
restore = c.save();
c.rotate(180.0F);
if (this.mClipToPadding) {
c.translate((float)(-this.getWidth() + this.getPaddingRight()), (float)(-this.getHeight() + this.getPaddingBottom()));
} else {
c.translate((float)(-this.getWidth()), (float)(-this.getHeight()));
}
needsInvalidate |= this.mBottomGlow != null && this.mBottomGlow.draw(c);
c.restoreToCount(restore);
}
if (!needsInvalidate && this.mItemAnimator != null && this.mItemDecorations.size() > 0 && this.mItemAnimator.isRunning()) {
needsInvalidate = true;
}
if (needsInvalidate) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
for(int i = 0; i < count; ++i) {
((RecyclerView.ItemDecoration)this.mItemDecorations.get(i)).onDrawOver(c, this, this.mState);
}
这段代码会绘制ItemDecoration中的onDrawOver内容,在绘制childView之后。
if (this.mLeftGlow != null && !this.mLeftGlow.isFinished()) {
restore = c.save();
width = this.mClipToPadding ? this.getPaddingBottom() : 0;
c.rotate(270.0F);
c.translate((float)(-this.getHeight() + width), 0.0F);
needsInvalidate = this.mLeftGlow != null && this.mLeftGlow.draw(c);
c.restoreToCount(restore);
}
if (this.mTopGlow != null && !this.mTopGlow.isFinished()) {
restore = c.save();
if (this.mClipToPadding) {
c.translate((float)this.getPaddingLeft(), (float)this.getPaddingTop());
}
needsInvalidate |= this.mTopGlow != null && this.mTopGlow.draw(c);
c.restoreToCount(restore);
}
if (this.mRightGlow != null && !this.mRightGlow.isFinished()) {
restore = c.save();
width = this.getWidth();
int padding = this.mClipToPadding ? this.getPaddingTop() : 0;
c.rotate(90.0F);
c.translate((float)(-padding), (float)(-width));
needsInvalidate |= this.mRightGlow != null && this.mRightGlow.draw(c);
c.restoreToCount(restore);
}
if (this.mBottomGlow != null && !this.mBottomGlow.isFinished()) {
restore = c.save();
c.rotate(180.0F);
if (this.mClipToPadding) {
c.translate((float)(-this.getWidth() + this.getPaddingRight()), (float)(-this.getHeight() + this.getPaddingBottom()));
} else {
c.translate((float)(-this.getWidth()), (float)(-this.getHeight()));
}
needsInvalidate |= this.mBottomGlow != null && this.mBottomGlow.draw(c);
c.restoreToCount(restore);
}
而这些代码都是用来绘制边界阴影效果的;
onDraw()
public void onDraw(Canvas c) {
super.onDraw(c);
int count = this.mItemDecorations.size();
for(int i = 0; i < count; ++i) {
((RecyclerView.ItemDecoration)this.mItemDecorations.get(i)).onDraw(c, this, this.mState);
}
}
其实onDraw没有处理什么逻辑~‘
主要是处理了ItemDecoration中的onDraw内容,在绘制childView之前。
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;
}
TraceCompat.beginSection("RV OnLayout");
TraceCompat.endSection();
这两段代码绘制性能分析会用到!
主要还是看dispatchLayout()这一方法
void dispatchLayout() {
if (this.mAdapter == null) {
Log.e("RecyclerView", "No adapter attached; skipping layout");
} else if (this.mLayout == null) {
Log.e("RecyclerView", "No layout manager attached; skipping layout");
} else {
this.mState.mIsMeasuring = false;
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
this.mLayout.setExactMeasureSpecsFrom(this);
this.dispatchLayoutStep2();
} else if (!this.mAdapterHelper.hasUpdates() && this.mLayout.getWidth() == this.getWidth() && this.mLayout.getHeight() == this.getHeight()) {
this.mLayout.setExactMeasureSpecsFrom(this);
} else {
this.mLayout.setExactMeasureSpecsFrom(this);
this.dispatchLayoutStep2();
}
this.dispatchLayoutStep3();
}
}
这面主要是做了如果onMeasure()方法里面没有进行childView的添加,这面会进行添加,也满足了onMeasure()
方法中if()这一方法里面的布局填充!
dispatchLayoutStep3()这一方法主要是保存信息,触发动画,清除垃圾,
private void dispatchLayoutStep3() {
this.mState.assertLayoutStep(4);
this.startInterceptRequestLayout();
this.onEnterLayoutOrScroll();
this.mState.mLayoutStep = 1;
if (this.mState.mRunSimpleAnimations) {
for(int i = this.mChildHelper.getChildCount() - 1; i >= 0; --i) {
RecyclerView.ViewHolder holder = getChildViewHolderInt(this.mChildHelper.getChildAt(i));
if (!holder.shouldIgnore()) {
long key = this.getChangedHolderKey(holder);
RecyclerView.ItemAnimator.ItemHolderInfo animationInfo = this.mItemAnimator.recordPostLayoutInformation(this.mState, holder);
RecyclerView.ViewHolder oldChangeViewHolder = this.mViewInfoStore.getFromOldChangeHolders(key);
if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
boolean oldDisappearing = this.mViewInfoStore.isDisappearing(oldChangeViewHolder);
boolean newDisappearing = this.mViewInfoStore.isDisappearing(holder);
if (oldDisappearing && oldChangeViewHolder == holder) {
//回归状态
this.mViewInfoStore.addToPostLayout(holder, animationInfo);
} else {
RecyclerView.ItemAnimator.ItemHolderInfo preInfo = this.mViewInfoStore.popFromPreLayout(oldChangeViewHolder);
this.mViewInfoStore.addToPostLayout(holder, animationInfo);
RecyclerView.ItemAnimator.ItemHolderInfo postInfo = this.mViewInfoStore.popFromPostLayout(holder);
if (preInfo == null) {
this.handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
} else {
//对于数据改变之后的动画有关代码!
this.animateChange(oldChangeViewHolder, holder, preInfo, postInfo, oldDisappearing, newDisappearing);
}
}
} else {
this.mViewInfoStore.addToPostLayout(holder, animationInfo);
}
}
}
this.mViewInfoStore.process(this.mViewInfoProcessCallback);
}
this.mLayout.removeAndRecycleScrapInt(this.mRecycler);
this.mState.mPreviousLayoutItemCount = this.mState.mItemCount;
this.mDataSetHasChangedAfterLayout = false;
this.mDispatchItemsChangedEvent = false;
this.mState.mRunSimpleAnimations = false;
this.mState.mRunPredictiveAnimations = false;
this.mLayout.mRequestedSimpleAnimations = false;
if (this.mRecycler.mChangedScrap != null) {
this.mRecycler.mChangedScrap.clear();
}
if (this.mLayout.mPrefetchMaxObservedInInitialPrefetch) {
this.mLayout.mPrefetchMaxCountObserved = 0;
this.mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
this.mRecycler.updateViewCacheSize();
}
this.mLayout.onLayoutCompleted(this.mState);
this.onExitLayoutOrScroll();
this.stopInterceptRequestLayout(false);
this.mViewInfoStore.clear();
if (this.didChildRangeChange(this.mMinMaxLayoutPositions[0], this.mMinMaxLayoutPositions[1])) {
this.dispatchOnScrolled(0, 0);
}
this.recoverFocusFromState();
this.resetFocusInfo();
}
this.mViewInfoStore.process(this.mViewInfoProcessCallback);
如上所示,当dispatchLayoutStep3() 这一方法for循环结束之后就会调用mViewInfoStore.process(this.mViewInfoProcessCallback)执行ItemAnimator动画! 剩下的代码在做一些回归的初始化!
这样的话RecyclerView的 onMeasure ,onDraw,onLayout方法就分析完了!其实有一些代码属性也不怎么清楚!毕竟里里面还是蛮复杂的,低耦合的代码!