每次更新View都會想到requestLayout()方法洽糟,所以想看看它的流程是怎樣的。
我一直覺得堕战,先理清楚步驟再去看源碼會好很多坤溃,所以先給出requestLayout()的一個調(diào)用流程:
- View#requestLayout()
- ViewGroup#requestLayout()
- ViewRootImpl#requestLayout()
- ViewRootImpl#scheduleTraversals()
- ViewRootImpl#doTraversal()
- ViewRootImpl#performTraversals()
最后ViewRootImpl#performTraversals()會依次調(diào)用performMeasure() , performLayout() , performDraw()。
requestLayout()是在View中定義的践啄,View#requestLayout():
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
/**記住這里設(shè)置的標(biāo)志位浇雹,非常重要...**/
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
/**畫重點,這里調(diào)用父類的requestLayout屿讽,一直往上循環(huán)...**/
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
ViewParent是一個接口昭灵,定義了view的一些操作等方法。ViewGroup就是一個ViewParent伐谈,所以mParent.requestLayout會一直往上遍歷烂完,而終點是ViewRootImpl,ViewRootImpl#requestLayout():
// 入口方法诵棵,接下來執(zhí)行 scheduleTraversals();
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
// 會執(zhí)行mTraversalRunnable抠蚣,它是一個Runnable
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
// 諾,TraversalRunnable 的定義履澳。所以最后執(zhí)行了doTraversal();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
// 重點還不在這嘶窄,重點是 performTraversals();!>啻1濉!
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
......
}
}
performTraversals()真的是一個很長很長的方法...忠蝗,我平時寫的一個類一般都不會有這么多现横。所以非常難去理解里面到底做了什么,所以我只是看了個大概的流程。
private void performTraversals() {
final View host = mView;
if (host == null || !mAdded)
return;
...... // 此處省略了很多行戒祠。骇两。。
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
/**終于出現(xiàn)了:performMeasure**/
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
}
}
} else {
maybeHandleWindowMove(frame);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
/**這里是performLayout**/
performLayout(lp, mWidth, mHeight);
......
}
...... // 此處省略了很多行姜盈。低千。。
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
/**這里是performDraw()**/
performDraw();
} else {
......
}
mIsInTraversal = false;
}
本來想看看performTraversals()具體執(zhí)行了些什么贩据,后來我放棄了...栋操,我只是找出了跟View繪制相關(guān)的三個方法:performMeasure() performLayout() performDraw()。
ViewRootImpl#performMeasure():
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
直接就調(diào)用了view的measure方法饱亮,從ViewRootImpl回到了View矾芙。View#measure():
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
/**還記得requestLayout方法里有把mPrivateFlags = PFLAG_FORCE_LAYOUT**/
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
/**依據(jù)forceLayout 和needsLayout決定是否要執(zhí)行onMeasure**/
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
/**出現(xiàn)了onMeasure**/
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
/**記住這里設(shè)置的標(biāo)識位**/
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
......
}
調(diào)用requestLayout時,設(shè)置了標(biāo)識位mPrivateFlags = PFLAG_FORCE_LAYOUT近上,最終這個view會根據(jù)mPrivateFlags 來判斷是否要執(zhí)行onMeasure方法剔宪。
那么最后還mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;,這個接下來onLayout會需要壹无。
ViewRootImpl#performLayout()
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
......
final View host = mView;
if (host == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
......
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
host是view葱绒,那么performLayout就執(zhí)行了view的layout(),View#layout():
public void layout(int l, int t, int r, int b) {
......
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
......
}
......
}
這里根據(jù)條件執(zhí)行onLayout斗锭,上一步執(zhí)行measure時地淀,mPrivateFlags |= PFLAG_LAYOUT_REQUIRED。
ViewRootImpl#performDraw():
private void performDraw() {
......
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
// 省略了很多行的代碼岖是,心好累帮毁。。豺撑。
private void draw(boolean fullRedrawNeeded) {
......
mAttachInfo.mDrawingTime =
mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
......
} else {
......
/**你敢信onDraw的邏輯竟然隱藏在這里烈疚?**/
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
......
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
try {
.....
// 意外找到了Canvas的賦值地點
canvas = mSurface.lockCanvas(dirty);
.....
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
}
try {
......
try {
......
/**終于找到你**/
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
......
}
} finally {
......
}
return true;
}
因此,邏輯是:performDraw()->draw()->drawSoftware()->view.draw()聪轿。
View#draw():
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
* 好貼心的注釋
* 1. Draw the background (繪制背景)
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content (繪制自身)
* 4. Draw children (繪制子view)
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
注釋真好爷肝,這個方法的邏輯一下子就看懂了。
總結(jié)
- 當(dāng)我們調(diào)用requestLayout時陆错,會執(zhí)行parent的requestLayout灯抛,最終執(zhí)行到ViewRootImpl的requestLayout。
- ViewRootImpl經(jīng)過一系列方法的調(diào)用執(zhí)行performTraversals()方法音瓷。
- performTraversals()會依次執(zhí)行performMeasure() performLayout() performDraw()方法牧愁。
看起來很簡單,但真的會被源碼轉(zhuǎn)暈外莲。而且實際調(diào)試過程中發(fā)現(xiàn),View的draw()方法不一定是drawSoftware()執(zhí)行的,可以看看ThreadedRenderer#draw()偷线。
mPrivateFlags 挺重要的磨确,會根據(jù)它的值來決定一些方法是否要調(diào)用。
其實忽略了的代碼中声邦,隱藏著很重要的邏輯乏奥,主要是對draw()方法的執(zhí)行判斷。因為代碼通篇看下來執(zhí)行requestLayout后亥曹,onMeasure(),onLayout(),onDraw()方法會依次執(zhí)行邓了,onMeasure()和onLayout()執(zhí)行是可以肯定的。但是onDraw()就不一定了媳瞪。這部分我還沒看懂骗炉,果然源碼不簡單。蛇受。句葵。