因爲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方法就分析完了!其實有一些代碼屬性也不怎麼清楚!畢竟裏裏面還是蠻複雜的,低耦合的代碼!