1. invalidate
/**
* Indicates that this view was specifically invalidated, not just dirtied because some
* child view was invalidated. The flag is used to determine when we need to recreate
* a view's display list (as opposed to just returning a reference to its existing
* display list).
*
* @hide
*/
static final int PFLAG_INVALIDATED = 0x80000000;
static final int PFLAG_DRAWING_CACHE_VALID = 0x00008000;
看起来好像,子view invalidate
就会导致父view PFLAG_INVALIDATED
但实际上,子view invalidate
只会导致自身 PFLAG_INVALIDATED
,父view的PFLAG_INVALIDATED
不会被设置。
而PFLAG_DRAWING_CACHE_VALID
代表drawing_cache
有效了,这个才是其中某个子view invalidate
了,一个子view invalidate
会导致父view的PFLAG_DRAWING_CACHE_VALID
被置0
,父view的父view也会PFLAG_DRAWING_CACHE_VALID
被置为0
,后文会分析
1.1 View#invalidateInternal
public void invalidate() {
invalidate(true);
}
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 (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
//这里判断该子View是否可见或者是否处于动画中
if (skipInvalidate()) {
return;
}
// Reset content capture caches
mCachedContentCaptureSession = null;
//根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘
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;
}
//设置PFLAG_DIRTY标记位
mPrivateFlags |= PFLAG_DIRTY;
//invalidateCache一般为true
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
//把需要重绘的区域传递给父容器
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);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
}
}
invalidate
有多个重载方法,但最终都会调用invalidateInternal
方法,在这个方法内部,进行了一系列的判断,判断View
是否需要重绘,接着为该View
设置标记位,然后把需要重绘的区域传递给父容器,即调用父容器的invalidateChild
方法。
注意其中:
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
2个标志位PFLAG_INVALIDATED
和PFLAG_DRAWING_CACHE_VALID
,PFLAG_INVALIDATED
置为1,PFLAG_DRAWING_CACHE_VALID
置为0。
1.2 ViewGroup#invalidateChild
public final void invalidateChild(View child, final Rect dirty) {
...
//设置 parent 等于自身
ViewParent parent = this;
if (attachInfo != null) {
final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
Matrix childMatrix = child.getMatrix();
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
//储存子View的mLeft和mTop值
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
...
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
} else if (parent instanceof ViewRootImpl) {
((ViewRootImpl) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
//对当前View的标记位进行设置
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
}
}
//调用ViewGroup的invalidateChildInParent
//如果已经达到最顶层view,则调用ViewRootImpl的invalidateChildInParent。
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
}
可以看到,在该方法内部,先设置当前视图的标记位,接着有一个do-while
循环,不停调用父view
的invalidateChildInParent
,一直到调用ViewRootImpl
的invalidateChildInParent
,作用就是不断向上回溯父容器,求得父容器和子View需要重绘的区域的并集(dirty),当父容器不是ViewRootImpl的时候,调用的是ViewGroup的invalidateChildInParent方法。
注意其中:
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
把PFLAG_DRAWING_CACHE_VALID
置为0,在do-while
循环后,当前view的所有父view,父view的父view。。。都会被PFLAG_DRAWING_CACHE_VALID
置为0。
1.3 ViewGroup # invalidateChildInParent
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
// either DRAWN, or DRAWING_CACHE_VALID
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
!= FLAG_OPTIMIZE_INVALIDATE) {
//将dirty中的坐标转化为父容器中的坐标,考虑mScrollX和mScrollY的影响
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
location[CHILD_TOP_INDEX] - mScrollY);
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
//求并集,结果是把子视图的dirty区域转化为父容器的dirty区域
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
final int left = mLeft;
final int top = mTop;
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
dirty.setEmpty();
}
}
//记录当前视图的mLeft和mTop值,在下一次循环中会把当前值再向父容器的坐标转化
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
} else {
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
} else {
// in case the dirty rect extends outside the bounds of this container
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
location[CHILD_LEFT_INDEX] = mLeft;
location[CHILD_TOP_INDEX] = mTop;
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
//返回当前视图的父容器
return mParent;
}
return null;
}
可以看出,调用invalidateChildInParent
会传进去一个Rect
叫dirty
,代表子窗口需要刷新的Rect
,调用offset
方法,把当前dirty区域的坐标转化为父容器中的坐标,然后父窗口会根据这个Rect
和父窗口本身做并集union
,从而得到父窗口需要刷新的Rect
区域,然后再传给父窗口的父窗口,一直递归直到ViewRootImpl
。
换句话说,dirty区域变成父容器区域。最后返回当前视图的父容器,以便进行下一次循环。
1.4 ViewRootImpl # invalidateChildInParent
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
//这个方法会去调用scheduleTraversals方法
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
// Add the new dirty rect to the current one
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
可以看出,该方法所做的工作与上面的差不多,都进行了offset
和union
对坐标的调整,然后把dirty
区域的信息保存在mDirty
中,最后调用了invalidateRectOnScreen
方法,这个方法会去调用scheduleTraversals
方法,触发View
的工作流程,之前梳理过View
的绘制流程,performTraversals方法中是有performMeasure
,performLayout()
,performDraw()
三个绘制方法的。但是,这里由于没有添加measure
和layout
的标记位,因此measure
、layout
流程不会执行,而是直接从draw
流程开始。
稍微捋一下scheduleTraversals
后的方法,
-
mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)
执行mTraversalRunnable
这个Runnable
- 执行run方法里的执行
doTraversal
方法 - 执行
performTraversals
- 执行
performDraw
- 执行
draw
- 执行
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this)
,mThreadedRenderer是一个ThreadedRenderer
-
ThreadedRenderer
类中的draw
方法继续调用updateRootDisplayList
方法 - 执行
updateViewTreeDisplayList
方法 - 执行
updateDisplayListIfDirty
方法 - if 是父族view 或者是 invalidate的view本身,进去调用
dispatchGetDisplayList
方法,反之进else,设设标识就完事了
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
是DecorView
,DecorView
会往下分发找到需要重绘的view,然后调用此view的draw
方法。假设调用invalidate
的view为a,a的parent为ap。我们知道此时a的PFLAG_DRAWING_CACHE_VALID
为0,PFLAG_INVALIDATED
为1。而a的父族view,ap,app,appp等的PFLAG_DRAWING_CACHE_VALID
为0,PFLAG_INVALIDATED
为0.
所以上边会把mRecreateDisplayList
设置为false
,后面会知道mRecreateDisplayList
代表了哪个view要被重绘的标记(在View
的updateDisplayListIfDirty
方法中就会把它置为true
),只有invalidate
的view a的mRecreateDisplayList
为true
,其他都是false
。
再来看DecorView
如何分发的。DecorView
的updateViewTreeDisplayList
会调updateDisplayListIfDirty
,如下,因为DecorView
是a的父族view,所以会进@AAA
的if,然后mRecreateDisplayList
为false
,所以会进@BBB
调用 dispatchGetDisplayList
,而invalidate的view a却进不去@BBB,因为它的mRecreateDisplayList是true,所以它会走到下面的try方法体里,进行重绘。
View # updateDisplayListIfDirty
public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if (!canHaveDisplayList()) {
// can't populate RenderNode, don't try
return renderNode;
}
//@AAA
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.hasDisplayList()
|| (mRecreateDisplayList)) {
// Don't need to recreate the display list, just need to tell our
// children to restore/recreate theirs
//@BBB
if (renderNode.hasDisplayList()
&& !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
return renderNode; // no work needed
}
// If we got here, we're recreating it. Mark it as such to ensure that
// we copy in child display lists into ours in drawChild()
mRecreateDisplayList = true;
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType();
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 {
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
//@CCC
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
if (debugDraw()) {
debugDrawFocus(canvas);
}
} else {
draw(canvas);
}
}
} finally {
renderNode.endRecording();
setDisplayListProperties(renderNode);
}
} else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
return renderNode;
}
ViewGroup # dispatchGetDisplayList
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);
}
}
if (mOverlay != null) {
View overlayView = mOverlay.getOverlayView();
recreateChildDisplayList(overlayView);
}
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size();
for (int i = 0; i < disappearingCount; ++i) {
final View child = disappearingChildren.get(i);
recreateChildDisplayList(child);
}
}
}
private void recreateChildDisplayList(View child) {
child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
child.mPrivateFlags &= ~PFLAG_INVALIDATED;
child.updateDisplayListIfDirty();
child.mRecreateDisplayList = false;
}
dispatchGetDisplayList的作用主要就是一个for循环,内调用recreateChildDisplayList
,让子view recreate & display
。
总结
parent: updateDisplayListIfDirty
-> dispatchGetDisplayList
-> for() recreateChildDisplayList
child: recreateChildDisplayList
-> updateDisplayListIfDirty
我们在具体分析下各个view会如何表现,核心代码是updateDisplayListIfDirty,
父族view的执行流程是
parent: updateDisplayListIfDirty
-> dispatchGetDisplayList
-> for() recreateChildDisplayList
child: recreateChildDisplayList
-> updateDisplayListIfDirty
如果是a本身,那么@AAA
进去,但是由于a的mRecreateDisplayList
为true,所以进不去@BBB
会到下面的try方法块进行重绘,这就是invalidate
导致的分发过程
如果不是父族view也不是a本身,那么PFLAG_DRAWING_CACHE_VALID
为1,会走@AAA
的else设置标志位,然后结束,不会分发到子view。
所以整个过程中只有a调用了draw
方法进行重绘,这是PFLAG_INVALIDATED
标志位起的作用
ViewGroup的invalidate
我们再来看看ViewGroup的invalidate,刚才说了invalidate如果是个view,那就只有自己本身会draw,如果是ViewGroup呢?
因为一般的ViewGroup都是SKIP_DRAW的,所以会走到@CCC
的dispatchDraw,
dispatchDraw的实现一般在
ViewGroup里,就是**调用子view的draw(注意是调用的3参的draw方法,而不是单参的方法)** 所以一般来说
ViewGroup的
invalidate`就是对子view进行重绘。(android.view.View#draw(android.graphics.Canvas, android.view.ViewGroup, long))
用人话说:当子View调用了invalidate
方法后,会为该View添加一个标记位,同时不断向父容器请求刷新,父容器通过计算得出自身需要重绘的区域,直到传递到ViewRootImpl
中,最终触发performTraversals
方法,进行开始View树重绘流程(只绘制需要重绘的视图)。
- view的
invalidate
并不会调用ViewRootImpl
的invalidate
。 -
performDraw
的过程中,大部分view的updateDisplayListIfDirty
都会被调用,但是只有设了标志位的view会调用draw方法进而调用onDraw
PostInvalidate
这个方法与invalidate
方法的作用是一样的,都是使View树重绘,但两者的使用条件不同,postInvalidate
是在非UI线程中调用,invalidate
则是在UI线程中调用。
public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
由以上代码可以看出,只有attachInfo
不为null
的时候才会继续执行,即只有确保视图被添加到窗口的时候才会通知view树重绘,因为这是一个异步方法,如果在视图还未被添加到窗口就通知重绘的话会出现错误,所以这样要做一下判断。接着调用了ViewRootImpl#dispatchInvalidateDelayed
方法:
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
这里用了Handler,发送了一个异步消息到主线程,显然这里发送的是MSG_INVALIDATE,即通知主线程刷新视图,具体的实现逻辑我们可以看看该mHandler的实现:
final ViewRootHandler mHandler = new ViewRootHandler();
final class ViewRootHandler extends Handler {
@Override
public String getMessageName(Message message) {
....
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
...
}
}
}
可以看出,参数message传递过来的正是View视图的实例,然后直接调用了invalidate方法,然后继续invalidate流程。
requestLayout
从方法名字可以知道,“请求布局”,那就是说,如果调用了这个方法,那么对于一个子View来说,应该会重新进行布局流程。但是,真实情况略有不同,如果子View调用了这个方法,其实会从View树重新进行一次测量、布局、绘制这三个流程,最终就会显示子View的最终情况。那么,这个方法是怎么实现的呢?我们从源码角度进行解析。
View # requestLayout
//从源码注释可以看出,如果当前View在请求布局的时候,View树正在进行布局流程的话,
//该请求会延迟到布局流程完成后或者绘制流程完成且下一次布局发现的时候再执行。
@CallSuper
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;
}
//为当前view设置标记位 PFLAG_FORCE_LAYOUT
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
//向父容器请求布局
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
在requestLayout
方法中,首先先判断当前View树是否正在布局流程,接着为当前子View设置标记位,该标记位的作用就是标记了当前的View是需要进行重新布局的,接着调用mParent.requestLayout
方法,这个十分重要,因为这里是向父容器请求布局,即调用父容器的requestLayout
方法,为父容器添加PFLAG_FORCE_LAYOUT
标记位,而父容器又会调用它的父容器的requestLayout
方法,即requestLayout
事件层层向上传递,直到DecorView,即根View
,而根View
又会传递给ViewRootImpl
,也即是说子View的requestLayout
事件,最终会被ViewRootImpl
接收并得到处理。纵观这个向上传递的流程,其实是采用了责任链模式,即不断向上传递该事件,直到找到能处理该事件的上级,在这里,只有ViewRootImpl
能够处理requestLayout
事件。
ViewRootImpl # requestLayout:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
我们可以简单的认为mLayoutRequested
为true
会触发performMeasure
(内部会调用onMeasure
)和performLayout
(内部会调用onLayout
)。然后在performDraw
内部draw
的过程中发现mDirty
为空,所以onDraw
不会被调用,不重绘。
这么看来requestLayout
不会导致onDraw
调用了?
也不见得,我们知道requestLayout
会导致performMeasure
和performLayout
,如果在layou
t过程中发现l
,t
,r
,b
和以前不一样,那就会触发一次invalidate
。代码在View
的setFrame
中,这个会在layout
时被调用。
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
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 our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
。。。
}
所以requestLayout
有可能会导致onDraw
被调用,也可能不导致onDraw
被调用,取决于view的l
,t
,r
,b
是否改变。
一些知识点:
- view不停找parent可以一直找到
DecorView
,按理说DecorView
是顶点了,但是DecorView
还有个虚拟父view:ViewRootImpl
。ViewRootImpl
不是一个View
或者ViewGroup
,他有个成员mView是DecorView
,所有的操作从ViewRootImpl
开始自上而下分发 - view的
invalidate
不会导致ViewRootImpl
的invalidate
被调用,而是递归调用父view的invalidateChildInParent
,直到ViewRootImpl
的invalidateChildInParent
,然后触发peformTraversals
,会导致当前view被重绘,由于mLayoutRequested
为false
,不会导致onMeasure
和onLayout
被调用,而OnDraw
会被调用 - 一个view的
invalidate
会导致本身PFLAG_INVALIDATED
置1
,导致本身以及父族viewgroup的PFLAG_DRAWING_CACHE_VALID
置0
-
requestLayout
会直接递归调用父窗口的requestLayout
,直到ViewRootImpl
,然后触发peformTraversals
,由于mLayoutRequested
为true
,会导致onMeasure
和onLayout
被调用。不一定会触发onDraw
-
requestLayout
触发onDraw
可能是因为在在layout
过程中发现l
,t
,r
,b
和以前不一样,那就会触发一次invalidate
,所以触发了onDraw
,也可能是因为别的原因导致mDirty
非空(比如在跑动画) -
requestLayout
会导致自己以及父族view的PFLAG_FORCE_LAYOUT
和PFLAG_INVALIDATED
标志被设置。
7.一般来说,只要刷新的时候就调用invalidate
,需要重新measure
就调用requestLayout
,后面再跟个invalidate
(为了保证重绘),个人理解。
网友评论