概述 注:本文基于Android 10源码,为了文章的简洁性,引用源码的地方可能有所删减。
在更新 View 时我们常用到 requestLayout 和 invalidate 这两个方法,本文会根据源码分析一下这两个方法的区别和工作逻辑。在开始阅读之前可以先看看 Android-View绘制原理 和 Android-Choreographer工作原理 对 Android View 绘制流程有一个大概的理解。
无论是 requestLayout 还是 invalidate 方法最后都会调用到 ViewRootImpl.performTraversals 方法开始 View 的更新,因此先看一下这个方法的部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private void performTraversals () { final View host = mView; boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight); } if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null || mForceNextWindowRelayout) { if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged || updatedConfiguration) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); layoutRequested = true ; } } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); if (didLayout) { performLayout(lp, mWidth, mHeight); } boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; if (!cancelDraw && !newSurface) { performDraw(); } }
这个方法贼长,抽取关键部分可以大致明白 performTraversals 里会根据一些判断条件来执行 View 的 Measure, Layout, Draw 三大流程。
invalidate View.invalidate 先看一下 invalidate 这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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 (skipInvalidate()) { return ; } 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 (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; 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); } } } private boolean skipInvalidate () { return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null && (!(mParent instanceof ViewGroup) || !((ViewGroup) mParent).isViewTransitioning(this )); }
首先会通过 skipInvalidate 方法判断是否要跳过 invalidate 过程,如果同时满足以下条件则跳过:
View 不可见
当前没有运行动画
父 View 不是 ViewGroup 类型或者父 ViewGoup 不处于过渡态
接下来再判断是否需要重绘,如果满足以下任意一个条件则进行重绘:
View 已经绘制完成且具有边界
invalidateCache == true 且设置了 PFLAG_DRAWING_CACHE_VALID 标志位,即绘制缓存可用
没有设置 PFLAG_INVALIDATED 标志位,即没有被重绘过
fullInvalidate == true 且在 透明 和 不透明 之间发生了变化
在处理了一些标志位的逻辑后调用了父 View 的 invalidateChild 方法并将要重绘的区域 damage 传给父 View。于是接着看 ViewGroup.invalidateChild 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 ) { do { parent = parent.invalidateChildInParent(location, dirty); } while (parent != null ); } }
可以看到这里会根据是否开启了硬件加速而走不同的逻辑。
软件绘制 关闭硬件加速时会循环调用 parent.invalidateChildInParent 方法并将返回值赋给 parent 直到其为 null 时退出循环,于是可以看看 ViewGroup.invalidateChildInParent 方法:
1 2 3 4 5 6 7 8 9 10 public ViewParent invalidateChildInParent (final int [] location, final Rect dirty) { if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0 ) { mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; return mParent; } return null ; }
invalidateChildInParent 方法主要是对子 View 传递的 dirty 区域进行处理与运算并返回 mParent 对象。因此在 invalidateChild 方法中会循环逐层调用父 View 的 invalidateChildInParent 方法,最终来到顶层的 DecorView.invalidateChild 方法,其 mParent 是 ViewRootImpl, 根据 Android-Window机制源码解读 可以重新看看 ViewRootImpl.setView 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void setView (View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this ) { if (mView == null ) { mView = view; requestLayout(); view.assignParent(this ); } } } void assignParent (ViewParent parent) { if (mParent == null ) { mParent = parent; } else if (parent == null ) { mParent = null ; } else { throw new RuntimeException("view " + this + " being added, but" + " it already has a parent" ); } }
因此可以看出 DecorView 的 mParent 就是 ViewRootImpl 对象,于是再看看 ViewRootImpl.invalidateChildInParent 方法:
1 2 3 4 5 6 7 8 9 10 11 public ViewParent invalidateChildInParent (int [] location, Rect dirty) { checkThread(); invalidateRectOnScreen(dirty); return null ; } private void invalidateRectOnScreen (Rect dirty) { scheduleTraversals(); }
可以看到 ViewRootImpl.invalidateChildInParent 方法返回 null,因此 ViewGroup.invalidateChild 会退出循环。至于 scheduleTraversals 方法应该很熟悉了,在 Vsync 信号到来后便会执行 performTraversals 方法(参考 Android-Choreographer工作原理 ),由于 mLayoutRequested 默认值是 false 且 invalidate 过程并没有给它赋值,于是不会调用 measureHierarchy 方法,而只有满足上面的条件 —— mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null ...
才会调用 performMeasure 和 performLayout,否则只会调用 performDraw 去重新 draw,并在其中给 View 设置 PFLAG_DRAWN 标志位。
硬件绘制 开启了硬件绘制后会走 ViewGroup.onDescendantInvalidated 方法:
1 2 3 4 5 6 public void onDescendantInvalidated (@NonNull View child, @NonNull View target) { if (mParent != null ) { mParent.onDescendantInvalidated(this , target); } }
和软件绘制类似,接下来会逐级调用父 View 的 onDescendantInvalidated 方法,最后走到 ViewRootImpl.onDescendantInvalidated 方法:
1 2 3 4 5 6 7 8 9 10 public void onDescendantInvalidated (@NonNull View child, @NonNull View descendant) { invalidate(); } void invalidate () { mDirty.set(0 , 0 , mWidth, mHeight); if (!mWillDrawSoon) { scheduleTraversals(); } }
可以看到跟软件绘制类似调用了 scheduleTraversals 方法,在 Vsync 信号到来后执行 performTraversals 方法,由于 mLayoutRequested 默认值是 false 且 invalidate 过程并没有给它赋值,于是不会调用 measureHierarchy 方法,而只有满足上面的条件 —— mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null ...
才会调用 performMeasure 和 performLayout,否则只会调用 performDraw 去重新 draw。
根据之前 Android-Surface原理解析及软硬件绘制 的解析,开启硬件加速后 performDraw 方法会通过 mAttachInfo.mThreadedRenderer.draw 来绘制,接着调用到 ThreadedRenderer.updateRootDisplayList 方法:
1 2 3 4 5 6 7 8 9 10 11 12 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 ; }
注意到在调用 View.invalidate 方法时对该 View 设置了 PFLAG_INVALIDATED 标志位,因此调用 invalidate 方法的 View 的 mRecreateDisplayList 属性为 true,其它 View 为 false。接下来会调用到 view.updateDisplayListIfDirty 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public RenderNode updateDisplayListIfDirty () { if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (mRecreateDisplayList)) { if (renderNode.isValid() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); return renderNode; } mRecreateDisplayList = true ; } return renderNode; }
这里根据 mRecreateDisplayList 的值会判断是走 重绘 还是 dispatchGetDisplayList 的逻辑:
对于调用 View.invalidate 方法的 View 来说其 mRecreateDisplayList 值为 true,因此走重绘逻辑。
其余 View 如 DecorView 等会走 dispatchGetDisplayList 逻辑。
最开始的根 View 是 DecorView 类型,来看看 dispatchGetDisplayList 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected void dispatchGetDisplayList () { 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 ; }
可以看到 recreateChildDisplayList 和 updateViewTreeDisplayList 方法有些类似,都会设置 mRecreateDisplayList 的值,于是从 DecorView 开始会遍历其子 View 依次调用 updateDisplayListIfDirty 方法,对于没有设置 PFLAG_INVALIDATED 标志位的 View 会调用 dispatchGetDisplayList 接着往下分发,而设置了 PFLAG_INVALIDATED 标志位的 View 则会执行重绘逻辑,根据 Android-View绘制原理 可知当该 View 执行 View.draw 开始重绘时,如果它是 ViewGroup 类型,则会调用 ViewGroup.dispatchDraw 方法分发 draw 事件,dispatchDraw 内部遍历子 View 然后调用 drawChild 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected boolean drawChild (Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this , drawingTime); } boolean draw (Canvas canvas, ViewGroup parent, long drawingTime) { boolean drawingWithRenderNode = mAttachInfo != null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas; if (drawingWithRenderNode) { renderNode = updateDisplayListIfDirty(); } }
小结 调用 View.invalidate() 方法后会逐级往上调用父 View 的相关方法,最终在 Choreographer 的控制下调用 ViewRootImpl.performTraversals() 方法。由于 mLayoutRequested == false,因此只有满足 mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null ...
等条件才会执行 measure 和 layout 流程,否则只执行 draw 流程,draw 流程的执行过程与是否开启硬件加速有关:
关闭硬件加速则从 DecorView 开始往下的所有子 View 都会被重新绘制。
开启硬件加速则只有调用 invalidate 方法的 View 才会重新绘制。
View 在绘制后会设置 PFLAG_DRAWN 标志位。
requestLayout View.requestLayout 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public void requestLayout () { if (mMeasureCache != null ) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null ) { 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()) { mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this ) { mAttachInfo.mViewRequestingLayout = null ; } } boolean requestLayoutDuringLayout (final View view) { if (!mLayoutRequesters.contains(view)) { mLayoutRequesters.add(view); } }
如果此时处于 Layout 则将该请求加入 ViewRootImpl 中的任务队列中,否则向上调用父 View 的 requestLayout 方法,直到 ViewRootImpl 中:
1 2 3 4 5 6 7 public void requestLayout () { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true ; scheduleTraversals(); } }
ViewRootImpl.requestLayout 方法在 check 了线程后将 mLayoutRequested 置为 true 且调用 scheduleTraversals 方法,于是在 Vsync 信号到来后会调用 performTraversals 方法。由于 mLayoutRequested == true,因此会依次执行 performMeasure, performLayout 以及 performDraw 方法开始 View 的绘制流程。
绘制过程 measure
接下来看看 View.requestLayout 方法对整个 View 树的影响。首先看一下 View.measure 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final void measure (int widthMeasureSpec, int heightMeasureSpec) { 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); if (forceLayout || needsLayout) { onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } }
在 View.requestLayout 方法中已经看到给当前 View 及其父 View 都添加了 PFLAG_FORCE_LAYOUT 标志位,因此其 forceLayout == ture,即会执行 onMeasure 方法测量。而对于未设置 PFLAG_FORCE_LAYOUT 标志位的 View 则需要判断其尺寸是否发生改变才会决定调用 onMeasure 与否。我们看到调用 onMeasure 后又设置了 PFLAG_LAYOUT_REQUIRED 标志位。
layout
接着看 View.layout 方法:
1 2 3 4 5 6 7 8 9 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); } }
由于调用 onMeasure 后设置了 PFLAG_LAYOUT_REQUIRED 标志位,因此也会跟着执行 onLayout 方法。另外看一下 setOpticalFrame 和 setFrame 方法,其中 setOpticalFrame 方法中最终也会调用到 setFrame 方法:
1 2 3 4 5 6 7 8 9 10 11 12 protected boolean setFrame (int left, int top, int right, int bottom) { boolean changed = false ; if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; int newWidth = right - left; int newHeight = bottom - top; boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); invalidate(sizeChanged); } }
因此可以看到当 View 四个顶点发生变化时也会调用 onLayout 方法,且会调用 View.invalidate 方法,并将 View 的宽高是否发生变化传给 invalidateCache 参数。
draw
ViewRootImpl.performDraw 会调用到 ViewRootImpl.draw 方法:
1 2 3 4 5 6 private boolean draw (boolean fullRedrawNeeded) { final Rect dirty = mDirty; if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { } }
dirty 是脏区,在 ViewRootImpl.invalidate 方法中会调用 mDirty.set() 方法为其设置边界值,如果上面 View 的顶点没有发生变化则不会调用 invalidate 方法,则 dirty.isEmpty()
返回 true,因此整个 View 树都不会重绘。
小结 调用 View.requestLayout 方法后会依次调用 performMeasure, performLayout 和 performDraw 方法,调用者 View 及其父 View 会从上往下重新进行 measure, layout 流程,一般情况下不会执行 draw 流程(子 View 会通过判断其尺寸/顶点是否发生改变而决定是否重新 measure/layout/draw 流程)。
示例 代码 接下来看一个示例,首先自定义两个 ViewGroup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MyViewGroup1(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) { constructor(context: Context?) : this (context, null , 0 ) constructor(context: Context?, attrs: AttributeSet?) : this (context, attrs, 0 ) override fun onMeasure (widthMeasureSpec: Int, heightMeasureSpec: Int) { Log.d("LLL" , "MyViewGroup1 -- onMeasure" ) super .onMeasure(widthMeasureSpec, heightMeasureSpec) } override fun onLayout (changed: Boolean, l: Int, t: Int, r: Int, b: Int) { Log.d("LLL" , "MyViewGroup1 -- onLayout" ) super .onLayout(changed, l, t, r, b) } override fun onDraw (canvas: Canvas?) { Log.d("LLL" , "MyViewGroup1 -- onDraw" ) super .onDraw(canvas) } }
MyViewGroup2 跟 MyViewGroup1 的代码类似。然后自定义一个 View:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class MyView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : View(context, attrs, defStyleAttr) { constructor(context: Context?) : this (context, null , 0 ) constructor(context: Context?, attrs: AttributeSet?) : this (context, attrs, 0 ) override fun onMeasure (widthMeasureSpec: Int, heightMeasureSpec: Int) { Log.d("LLL" , "MyView -- onMeasure" ) super .onMeasure(widthMeasureSpec, heightMeasureSpec) } override fun onLayout (changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { Log.d("LLL" , "MyView -- onLayout" ) super .onLayout(changed, left, top, right, bottom) } override fun onDraw (canvas: Canvas?) { Log.d("LLL" , "MyView -- onDraw" ) super .onDraw(canvas) } }
接下来看一下布局文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <com.hearing.demo.MyViewGroup1 xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="match_parent" android:background ="#FF0000" android:onClick ="group1" > <com.hearing.demo.MyViewGroup2 android:layout_width ="300dp" android:layout_height ="300dp" android:background ="#00FF00" android:onClick ="group2" > <com.hearing.demo.MyView android:layout_width ="150dp" android:layout_height ="150dp" android:background ="#0000FF" android:onClick ="view1" /> </com.hearing.demo.MyViewGroup2 > </com.hearing.demo.MyViewGroup1 >
给这三个控件添加点击事件,分别调用其 invalidate 和 requestLayout 方法。
invalidate 开启硬件加速
1 2 3 4 5 6 7 8 D/LLL: MyViewGroup1 -- onDraw D/LLL: MyViewGroup2 -- onDraw D/LLL: MyView -- onDraw
关闭硬件加速
1 2 3 4 5 6 7 8 9 10 11 12 13 14 D/LLL: MyViewGroup1 -- onDraw D/LLL: MyViewGroup2 -- onDraw D/LLL: MyView -- onDraw D/LLL: MyViewGroup1 -- onDraw D/LLL: MyViewGroup2 -- onDraw D/LLL: MyView -- onDraw D/LLL: MyViewGroup1 -- onDraw D/LLL: MyViewGroup2 -- onDraw D/LLL: MyView -- onDraw
requestLayout 开启硬件加速
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 D/LLL: MyViewGroup1 -- onMeasure D/LLL: MyViewGroup1 -- onLayout D/LLL: MyViewGroup1 -- onMeasure D/LLL: MyViewGroup2 -- onMeasure D/LLL: MyViewGroup1 -- onLayout D/LLL: MyViewGroup2 -- onLayout D/LLL: MyViewGroup1 -- onMeasure D/LLL: MyViewGroup2 -- onMeasure D/LLL: MyView -- onMeasure D/LLL: MyViewGroup1 -- onLayout D/LLL: MyViewGroup2 -- onLayout D/LLL: MyView -- onLayout
关闭硬件加速
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 D/LLL: MyViewGroup1 -- onMeasure D/LLL: MyViewGroup1 -- onLayout D/LLL: MyViewGroup1 -- onMeasure D/LLL: MyViewGroup2 -- onMeasure D/LLL: MyViewGroup1 -- onLayout D/LLL: MyViewGroup2 -- onLayout D/LLL: MyViewGroup1 -- onMeasure D/LLL: MyViewGroup2 -- onMeasure D/LLL: MyView -- onMeasure D/LLL: MyViewGroup1 -- onLayout D/LLL: MyViewGroup2 -- onLayout D/LLL: MyView -- onLayout
总结 invalidate
调用 View.invalidate() 方法后会逐级往上调用父 View 的相关方法,最终在 Choreographer 的控制下调用 ViewRootImpl.performTraversals() 方法。只有满足 mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null ...
等条件才会执行 measure 和 layout 流程,否则只执行 draw 流程,draw 流程的执行过程与是否开启硬件加速有关:
关闭硬件加速则从 DecorView 开始往下的所有子 View 都会被重新绘制。
开启硬件加速则只有调用 invalidate 方法的 View 才会重新绘制。
requestLayout
调用 View.requestLayout 方法后会依次调用 performMeasure, performLayout 和 performDraw 方法,调用者 View 及其父 View 会重新从上往下进行 measure, layout 流程,一般情况下不会执行 draw 流程(子 View 会通过判断其尺寸/顶点是否发生改变而决定是否重新 measure/layout/draw 流程)。
因此,当只需要进行重绘时可以使用 invalidate 方法,如果需要重新测量和布局则可以使用 requestLayout 方法,而 requestLayout 方法不一定会重绘,因此如果要进行重绘可以再手动调用 invalidate 方法。