每次更新View都会想到requestLayout()方法,所以想看看它的流程是怎样的。
我一直觉得,先理清楚步骤再去看源码会好很多,所以先给出requestLayout()的一个调用流程:
View#requestLayout()
ViewGroup#requestLayout()
ViewRootImpl#requestLayout()
ViewRootImpl#scheduleTraversals()
ViewRootImpl#doTraversal()
ViewRootImpl#performTraversals()
最后ViewRootImpl#performTraversals()会依次调用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;
} /**记住这里设置的标志位,非常重要...**/
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { /**画重点,这里调用父类的requestLayout,一直往上循环...**/
mParent.requestLayout();
} if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
ViewParent是一个接口,定义了view的一些操作等方法。ViewGroup就是一个ViewParent,所以mParent.requestLayout会一直往上遍历,而终点是ViewRootImpl,ViewRootImpl#requestLayout():
// 入口方法,接下来执行 scheduleTraversals();public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}// 会执行mTraversalRunnable,它是一个Runnablevoid scheduleTraversals() { if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}// 诺,TraversalRunnable 的定义。所以最后执行了doTraversal();final class TraversalRunnable implements Runnable { @Override
public void run() {
doTraversal();
}
}// 重点还不在这,重点是 performTraversals();!!!!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); /**终于出现了: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()具体执行了些什么,后来我放弃了…,我只是找出了跟View绘制相关的三个方法: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);
}
}
直接就调用了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); /**依据forceLayout 和needsLayout决定是否要执行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
/**出现了onMeasure**/
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
/**记住这里设置的标识位**/
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
......
}
调用requestLayout时,设置了标识位mPrivateFlags = PFLAG_FORCE_LAYOUT,最终这个view会根据mPrivateFlags 来判断是否要执行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就执行了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);
......
}
......
}
这里根据条件执行onLayout,上一步执行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;
}
注释真好,这个方法的逻辑一下子就看懂了。
总结
当我们调用requestLayout时,会执行parent的requestLayout,最终执行到ViewRootImpl的requestLayout。
ViewRootImpl经过一系列方法的调用执行performTraversals()方法。
performTraversals()会依次执行performMeasure() performLayout() performDraw()方法。
看起来很简单,但真的会被源码转晕。而且实际调试过程中发现,View的draw()方法不一定是drawSoftware()执行的,可以看看ThreadedRenderer#draw()。
mPrivateFlags 挺重要的,会根据它的值来决定一些方法是否要调用。
其实忽略了的代码中,隐藏着很重要的逻辑,主要是对draw()方法的执行判断。因为代码通篇看下来执行requestLayout后,onMeasure(),onLayout(),onDraw()方法会依次执行,onMeasure()和onLayout()执行是可以肯定的。但是onDraw()就不一定了。这部分我还没看懂,果然源码不简单。。。
作者:zcbiner
链接:https://www.jianshu.com/p/c25b497df8be
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
喜欢 就关注吧,欢迎投稿!
本网站文章均为原创内容,并可随意转载,但请标明本文链接
如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/requestlayout的执行流程/