提示:本文基于 Android API 31
ViewRootImpl
任何控件的展示都是通过 WindowManager.addView 来实现的并且 WindowManager 是一个接口真正的实现是 WindowManagerImpl 它又直接调用了 WindowManagerGlobal 的 addView 方法在这个方法里创建了 ViewRootImpl 它是所有控件的抽象父控件,它没有不是继承自 View 但是实现了 ViewParent 接口是 DecorView 的 parent,在 Activity 中通过 setContentView 方法传入的布局就是设置给 DecorView 的一个 id 为 android.R.id.content 的子控件的
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
在 ViewRootImpl 的 setView 方法里会调用 requestLayout() 方法触发测量、布局、绘制工作
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mTraversalRunnable 是 TraversalRunnable 对象在下一次 Vsync 信号来的时候会调用其 run 方法
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
performMeasure
performLayout
performDraw
}
performTraversals 方法中会一次调用测量、布局、绘制的方法,依次看一下
测量
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
DecorView 因为是顶层 View 没有实际父级只有抽象父级 ViewRootImpl 所以它的测量过程与普通控件有一些区别,其他控件的 MeasureSpec 都是通过父级的 MeasureSpec 和自己的 ViewGroup.LayoutParams 得到的而 DecorView 的 MeasureSpec 是通过屏幕的宽高和 WindowManager.LayoutParams 得到的,下面先来介绍一下 MeasureSpec
MeasureSpec 代表一个 32 位 int 值,高 2 位代表 SpecMode,低 30 位代表 SpecSize,SpecMode 是指测量模式,而 SpecSize 是指在某种测量模式下的规格大小。
SpecMode 有三类,每一类都表示特殊的含义,如下所示。
UNSPECIFIED:父容器不对 View 有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
EXACTLY:父容器已经检测出 View 所需要的精确大小,这个时候 View 的最终大小就是 SpecSize 所指定的值。它对应于 LayoutParams 中的 match_parent 和具体的数值这两种模式。
AT_MOST:父容器指定了一个可用大小即 SpecSize,View 的大小不能大于这个值,具体是什么值要看不同的 View 的具体实现。它对应于 LayoutParams 中的 wrap_content
《Android 开发艺术探索》
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
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);
}
}
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if (forceLayout || needsLayout) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
}
在view.measure()的方法里,仅当给与的MeasureSpec发生变化时,或要求强制重新布局时,才会进行测量。
强制重新布局 : 控件树中的一个子控件内容发生变化时,需要重新测量和布局的情况,在这种情况下,这个子控件的父控件(以及父控件的父控件)所提供的MeasureSpec必定与上次测量时的值相同,因而导致从ViewRootImpl到这个控件的路径上,父控件的measure()方法无法得到执行,进而导致子控件无法重新测量其布局和尺寸。
解决途径 : 因此,当子控件因内容发生变化时,从子控件到父控件回溯到ViewRootImpl,并依次调用父控件的requestLayout()方法。这个方法会在mPrivateFlags中加入标记PFLAG_FORCE_LAYOUT,从而使得这些父控件的measure()方法得以顺利执行,进而这个子控件有机会进行重新布局与测量。这便是强制重新布局的意义所在。
Android View的工作流程
measure 是 View 的 final 方法不可被子类重写,在这个方法里会调用 onMeasure 传入控件的 MeasureSpec 完成子控件的测量,ViewGroup 并没有重写 onMeasure 方法因为不同的 ViewGroup 的子类的测量规则是不一样的所以需要子类自己去实现,因为 DecorView 继承自 FrameLayout 所以看一下 FrameLayout 的 onMeasure 方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
FrameLayout 的 onMeasure 方法大致就是逐一测量所有的子控件然后使用最大的子控件的宽高设置自己的大小,如果有特殊情况则再测量一次子控件,看一下测量子控件的方法
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
measureChildWithMargins s是 ViewGroup 的方法,处理了间距调用 getChildMeasureSpec 方法,在 FrameLayout 需要二次测量的时候也调用了这个方法
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
方法已经详细注释了,具体逻辑可以通过一个表呈现:
右下角两项 UNSPECIFIED 为 0 或 parentSize 取决于 View 中静态变量 sUseZeroUnspecifiedMeasureSpec 的值,它的值是 sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < Build.VERSION_CODES.M
最后根据测量大小和测量模式封装成一个 MeasureSpec 然后去测量子控件,子控件的 measure 调用 onMeasure 方法接下来看一下 View 的 onMeasure 方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
通过 getDefaultSize 方法可以知道在 View 的默认实现中,不管是 EXACTLY 还是 AT_MOST 模式拿到的都是父控件的可用大小,这就是说在 xml 文件中不管是设置为 match_parent 还是 wrap_content 显示出来的大小都是一样的所以当自定义 View 的时候通常要重写 onMeasure 方法根据需求给一个 AT_MOST 模式下的默认大小
总结一下 View 的测量过程就是从 DecorView 开始,根据屏幕宽高和 WindowManager.LayoutParams(width, height 默认是 MATCH_PARENT)得到 MeasureSpec 然后去测量自己,因为自己是 ViewGroup 所以会先测量所有的子控件才能知道自己的大小,如果子控件是 View 测量就结束了如果是 ViewGroup 则重复这个过程,这里普通控件(非 DecorView 其他 View)与 DecorView 的区别是普通控件的 MeasureSpec 是通过父控件的 MeasureSpec 和自己的 ViewGroup.LayoutParams 得到的
布局
布局从 ViewRootImpl 的 performLayout 开始然后调用真正的顶级控件 DecorView 的 layout 方法,FrameLayout 并没有重写 layout 方法 ViewGroup 也是简单的调用了 super.layout 方法所以直接看 View 的 layout 方法
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
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);
}
}
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
mPrivateFlags |= PFLAG_HAS_BOUNDS;
if (sizeChanged) {
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
}
return changed;
}
所谓布局其实就是把自己在父控件的坐标记录下来,之后就可以通过 getWidth/getHeight 拿到宽高了,所以 getWidth/getHeight 与 getMeasureWidth/getMeasureHeight 的区别就是时机不同,getMeasureWidth/getMeasureHeight 是在测量完成后可以拿到 getWidth/getHeight 是在布局完成后可以拿到,并且结果通常都是一样的(可以在布局时处理成不一样但没有意义)
ViewGroup 中 onLayout 是抽象方法需要子类去根据自己的需要重写,在具体 ViewGroup 子类中会遍历所有的子控件,如果子控件是 View 流程结束如果子控件是 ViewGroup 则递归执行这个过程
绘制
private void performDraw() {
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
}
}
}
private boolean draw(boolean fullRedrawNeeded) {
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty || mNextDrawUseBlastSync) {
if (isHardwareEnabled()) {
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
final Canvas = mSurface.lockCanvas(dirty);
mView.draw(canvas);
}
public void draw(Canvas canvas) {
drawBackground(canvas);
onDraw(canvas);
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
onDrawForeground(canvas);
drawDefaultFocusHighlight(canvas);
}
View 的绘制流程主要是 onDraw 绘制自己 dispatchDraw 分发绘制子控件,对于自定义 View 不需要重写 dispatchDraw 需要重写 onDraw 绘制自己的逻辑,对于自定义 ViewGroup 一般不需要重写 onDraw 绘制自己(并且 ViewGroup 的 onDraw 默认情况下不执行)需要重写 dispatchDraw 分发绘制所有子控件,如果子控件也是 ViewGroup 重复此过程
protected void dispatchDraw(Canvas canvas) {
for (int i = 0; i < childrenCount; i++) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((RecordingCanvas) canvas).drawRenderNode(renderNode);
} else {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
}
}
mRecreateDisplayList = false;
return more;
}
总结一下未开启硬件加速的流程从 android.view.ViewRootImpl#performDraw 调用开始调用 android.view.ViewRootImpl#draw 通过 Surface 对象申请一块画布调用 com.android.internal.policy.DecorView#draw 传入这块画布先通过 onDraw 方法绘制自己(也可以通过设置不绘制自己)再调用 dispatchDraw 绘制所有的子控件如果子控件是 ViewGroup 则递归执行这个流程。在看硬件加速的流程之前先看一下 android.view.View#invalidate() 这个方法(postInvalidate 是在子线程使用通过 Handler 间接调用的 invalidate 方法)
invalidate
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
}
}
public final void invalidateChild(View child, final Rect dirty) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null && attachInfo.mHardwareAccelerated) {
onDescendantInvalidated(child, child);
return;
}
ViewParent parent = this;
if (attachInfo != null) {
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
}
}
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
if (mParent != null) {
mParent.onDescendantInvalidated(this, target);
}
}
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
invalidate();
}
void invalidate() {
scheduleTraversals();
}
从上面最开始的分析已经知道 scheduleTraversals 会在下一次 Vsync 信号来的时候调用 performTraversals 再调用 performDraw 触发重绘,如果开启了硬件加速则继续执行到 android.view.ThreadedRenderer#draw
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
updateRootDisplayList(view, callbacks);
}
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
updateViewTreeDisplayList(view);
}
private void updateViewTreeDisplayList(View view) {
view.mPrivateFlags |= View.PFLAG_DRAWN;
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
view.updateDisplayListIfDirty();
view.mRecreateDisplayList = false;
}
public RenderNode updateDisplayListIfDirty() {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.hasDisplayList()
|| (mRecreateDisplayList)) {
if (renderNode.hasDisplayList()
&& !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
return renderNode;
}
mRecreateDisplayList = true;
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
try {
if (layerType == LAYER_TYPE_SOFTWARE) {
buildDrawingCache(true);
Bitmap cache = getDrawingCache(true);
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
} else {
draw(canvas);
}
}
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
protected void dispatchGetDisplayList() {
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
recreateChildDisplayList(child);
}
}
}
private void recreateChildDisplayList(View child) {
child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
child.mPrivateFlags &= ~PFLAG_INVALIDATED;
child.updateDisplayListIfDirty();
child.mRecreateDisplayList = false;
}
总结一下当通过 invalidate 请求重绘时调用 invalidate 的子控件会添加 PFLAG_INVALIDATED 标记并且通过控件的 parent 逐级请求重绘直到调用到 ViewRootImpl 的 scheduleTraversals 方法在下一次 Vsync 信号来的时候执行真正的重绘操作 并且因为只有直接调用 invalidate 的控件设置了 PFLAG_INVALIDATED 标记 view.mRecreateDisplayList 为 true 会执行重绘,其他控件调用 dispatchGetDisplayList 分发子控件的重绘操作并且同样会根据是否添加了 PFLAG_INVALIDATED 标记判断是否执行重绘直至完成所有控件的重绘动作,如果是首次打开页面直接通过 android.view.ViewRootImpl#requestLayout 触发整个流程应该是所有的控件都会被重绘
简单来说当关闭硬件加速时所有子 View 都会被重新绘制,开启硬件加速时只有调用 invalidate 方法的 View 才会重新绘制
requestLayout
// android.view.View#requestLayout
public void requestLayout() {
// 代码省略 ..
// 设置强制布局标志
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
// 调用 parent requestLayout
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
// 代码省略 ..
}
最后看一下 View 的 requestLayout 方法确实都设置了 PFLAG_FORCE_LAYOUT 强制布局标记。invalidate 与 requestLayout 的区别是 invalidate 只会触发绘制操作不会触发测量、布局流程因为 invalidate 方法没有设置 PFLAG_FORCE_LAYOUT 标记,requestLayout 通常情况下只会触发测量、布局流程不会触发重绘流程,但如果控件大小改变则也会触发绘制流程。
总结:首次 View 工作流程是通过 android.view.ViewRootImpl#requestLayout 触发的,它调用了 scheduleTraversals 方法向 Choreographer 发送一个 Runnable 和一个同步屏障消息(保证优先执行这个 Runnable 异步消息)在下一次 Vsync 信号到来后执行这个 Runnable 执行 View 真正的工作测量、布局、绘制。测量的入口是 perfromMeasure 方法它会调用 DecorView 的 measure 从顶层 View 开始整个 View 树的测量,如果 View 只是 View 则只测量自己就结束了如果是 ViewGroup 要先测量所有子控件再测量自己如果子控件还是 ViewGroup 递归执行。布局的入口是 performLayout 方法它会调用 DecorView 的 layout 从顶层 View 开始整个 View 树的布局,如果 View 只是 View 则只布局自己就结束了如果是 ViewGroup 要先布局自己再递归布局所有子控件。绘制的入口是 performDraw 方法它会调用 DecorView 的 draw 开始整个 View 树的绘制,如果 View 只是 View 则只绘制自己就结束了如果是 ViewGroup 要先绘制自己(ViewGroup 默认不绘制自己)再递归绘制所有子控件,并且绘制还有是否开启硬件加速两种情况,当开启硬件加速时只绘制直接调用 invalidate 触发重绘的 View 当未开启硬件加速时绘制所有控件。
参考与感谢
比较一下requestLayout和invalidate方法
一文读懂 View 的 Measure、Layout、Draw 流程
Android View的工作流程
Android ViewGroup onDraw为什么没调用
|