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就會導(dǎo)致父view PFLAG_INVALIDATED
但實際上,子view invalidate只會導(dǎo)致自身 PFLAG_INVALIDATED,父view的PFLAG_INVALIDATED不會被設(shè)置。
而PFLAG_DRAWING_CACHE_VALID代表drawing_cache有效了,這個才是其中某個子view invalidate了,一個子view invalidate會導(dǎo)致父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;
//根據(jù)View的標(biāo)記位來判斷該子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;
}
//設(shè)置PFLAG_DIRTY標(biāo)記位
mPrivateFlags |= PFLAG_DIRTY;
//invalidateCache一般為true
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
//把需要重繪的區(qū)域傳遞給父容器
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);
//調(diào)用父容器的方法,向上傳遞事件
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有多個重載方法,但最終都會調(diào)用invalidateInternal方法,在這個方法內(nèi)部,進(jìn)行了一系列的判斷,判斷View是否需要重繪,接著為該View設(shè)置標(biāo)記位,然后把需要重繪的區(qū)域傳遞給父容器,即調(diào)用父容器的invalidateChild方法。
注意其中:
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
2個標(biāo)志位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) {
...
//設(shè)置 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) {
//對當(dāng)前View的標(biāo)記位進(jìn)行設(shè)置
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
}
}
//調(diào)用ViewGroup的invalidateChildInParent
//如果已經(jīng)達(dá)到最頂層view,則調(diào)用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);
}
}
可以看到,在該方法內(nèi)部,先設(shè)置當(dāng)前視圖的標(biāo)記位,接著有一個do-while循環(huán),不停調(diào)用父view的invalidateChildInParent,一直到調(diào)用ViewRootImpl的invalidateChildInParent,作用就是不斷向上回溯父容器,求得父容器和子View需要重繪的區(qū)域的并集(dirty),當(dāng)父容器不是ViewRootImpl的時候,調(diào)用的是ViewGroup的invalidateChildInParent方法。
注意其中:
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
把PFLAG_DRAWING_CACHE_VALID置為0,在do-while循環(huán)后,當(dāng)前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中的坐標(biāo)轉(zhuǎn)化為父容器中的坐標(biāo),考慮mScrollX和mScrollY的影響
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
location[CHILD_TOP_INDEX] - mScrollY);
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
//求并集,結(jié)果是把子視圖的dirty區(qū)域轉(zhuǎn)化為父容器的dirty區(qū)域
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();
}
}
//記錄當(dāng)前視圖的mLeft和mTop值,在下一次循環(huán)中會把當(dāng)前值再向父容器的坐標(biāo)轉(zhuǎn)化
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;
}
//返回當(dāng)前視圖的父容器
return mParent;
}
return null;
}
可以看出,調(diào)用invalidateChildInParent會傳進(jìn)去一個Rect叫dirty,代表子窗口需要刷新的Rect,調(diào)用offset方法,把當(dāng)前dirty區(qū)域的坐標(biāo)轉(zhuǎn)化為父容器中的坐標(biāo),然后父窗口會根據(jù)這個Rect和父窗口本身做并集union,從而得到父窗口需要刷新的Rect區(qū)域,然后再傳給父窗口的父窗口,一直遞歸直到ViewRootImpl。
換句話說,dirty區(qū)域變成父容器區(qū)域。最后返回當(dāng)前視圖的父容器,以便進(jìn)行下一次循環(huán)。
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);
}
}
//這個方法會去調(diào)用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();
}
}
可以看出,該方法所做的工作與上面的差不多,都進(jìn)行了offset和union對坐標(biāo)的調(diào)整,然后把dirty區(qū)域的信息保存在mDirty中,最后調(diào)用了invalidateRectOnScreen方法,這個方法會去調(diào)用scheduleTraversals方法,觸發(fā)View的工作流程,之前梳理過View的繪制流程,performTraversals方法中是有performMeasure,performLayout(),performDraw()三個繪制方法的。但是,這里由于沒有添加measure和layout的標(biāo)記位,因此measure、layout流程不會執(zhí)行,而是直接從draw流程開始。
稍微捋一下scheduleTraversals后的方法,
-
mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)執(zhí)行mTraversalRunnable這個Runnable - 執(zhí)行run方法里的執(zhí)行
doTraversal方法 - 執(zhí)行
performTraversals - 執(zhí)行
performDraw - 執(zhí)行
draw - 執(zhí)行
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this),mThreadedRenderer是一個ThreadedRenderer -
ThreadedRenderer類中的draw方法繼續(xù)調(diào)用updateRootDisplayList方法 - 執(zhí)行
updateViewTreeDisplayList方法 - 執(zhí)行
updateDisplayListIfDirty方法 - if 是父族view 或者是 invalidate的view本身,進(jìn)去調(diào)用
dispatchGetDisplayList方法,反之進(jìn)else,設(shè)設(shè)標(biāo)識就完事了
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會往下分發(fā)找到需要重繪的view,然后調(diào)用此view的draw方法。假設(shè)調(diào)用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設(shè)置為false,后面會知道mRecreateDisplayList代表了哪個view要被重繪的標(biāo)記(在View的updateDisplayListIfDirty方法中就會把它置為true),只有invalidate的view a的mRecreateDisplayList為true,其他都是false。
再來看DecorView如何分發(fā)的。DecorView的updateViewTreeDisplayList會調(diào)updateDisplayListIfDirty,如下,因為DecorView是a的父族view,所以會進(jìn)@AAA的if,然后mRecreateDisplayList為false,所以會進(jìn)@BBB調(diào)用 dispatchGetDisplayList,而invalidate的view a卻進(jìn)不去@BBB,因為它的mRecreateDisplayList是true,所以它會走到下面的try方法體里,進(jìn)行重繪。
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循環(huán),內(nèi)調(diào)用recreateChildDisplayList,讓子view recreate & display。
總結(jié)
parent: updateDisplayListIfDirty -> dispatchGetDisplayList -> for() recreateChildDisplayList
child: recreateChildDisplayList -> updateDisplayListIfDirty
我們在具體分析下各個view會如何表現(xiàn),核心代碼是updateDisplayListIfDirty,
父族view的執(zhí)行流程是
parent: updateDisplayListIfDirty -> dispatchGetDisplayList -> for() recreateChildDisplayList
child: recreateChildDisplayList -> updateDisplayListIfDirty
如果是a本身,那么@AAA進(jìn)去,但是由于a的mRecreateDisplayList為true,所以進(jìn)不去@BBB會到下面的try方法塊進(jìn)行重繪,這就是invalidate導(dǎo)致的分發(fā)過程
如果不是父族view也不是a本身,那么PFLAG_DRAWING_CACHE_VALID為1,會走@AAA的else設(shè)置標(biāo)志位,然后結(jié)束,不會分發(fā)到子view。
所以整個過程中只有a調(diào)用了draw方法進(jìn)行重繪,這是PFLAG_INVALIDATED標(biāo)志位起的作用
ViewGroup的invalidate
我們再來看看ViewGroup的invalidate,剛才說了invalidate如果是個view,那就只有自己本身會draw,如果是ViewGroup呢?
因為一般的ViewGroup都是SKIP_DRAW的,所以會走到@CCC的dispatchDraw,dispatchDraw的實現(xiàn)一般在ViewGroup里,就是**調(diào)用子view的draw(注意是調(diào)用的3參的draw方法,而不是單參的方法)** 所以一般來說ViewGroup的invalidate`就是對子view進(jìn)行重繪。(android.view.View#draw(android.graphics.Canvas, android.view.ViewGroup, long))
用人話說:當(dāng)子View調(diào)用了invalidate方法后,會為該View添加一個標(biāo)記位,同時不斷向父容器請求刷新,父容器通過計算得出自身需要重繪的區(qū)域,直到傳遞到ViewRootImpl中,最終觸發(fā)performTraversals方法,進(jìn)行開始View樹重繪流程(只繪制需要重繪的視圖)。
- view的
invalidate并不會調(diào)用ViewRootImpl的invalidate。 -
performDraw的過程中,大部分view的updateDisplayListIfDirty都會被調(diào)用,但是只有設(shè)了標(biāo)志位的view會調(diào)用draw方法進(jìn)而調(diào)用onDraw
PostInvalidate
這個方法與invalidate方法的作用是一樣的,都是使View樹重繪,但兩者的使用條件不同,postInvalidate是在非UI線程中調(diào)用,invalidate則是在UI線程中調(diào)用。
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的時候才會繼續(xù)執(zhí)行,即只有確保視圖被添加到窗口的時候才會通知view樹重繪,因為這是一個異步方法,如果在視圖還未被添加到窗口就通知重繪的話會出現(xiàn)錯誤,所以這樣要做一下判斷。接著調(diào)用了ViewRootImpl#dispatchInvalidateDelayed方法:
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
這里用了Handler,發(fā)送了一個異步消息到主線程,顯然這里發(fā)送的是MSG_INVALIDATE,即通知主線程刷新視圖,具體的實現(xiàn)邏輯我們可以看看該mHandler的實現(xiàn):
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;
...
}
}
}
可以看出,參數(shù)message傳遞過來的正是View視圖的實例,然后直接調(diào)用了invalidate方法,然后繼續(xù)invalidate流程。
requestLayout
從方法名字可以知道,“請求布局”,那就是說,如果調(diào)用了這個方法,那么對于一個子View來說,應(yīng)該會重新進(jìn)行布局流程。但是,真實情況略有不同,如果子View調(diào)用了這個方法,其實會從View樹重新進(jìn)行一次測量、布局、繪制這三個流程,最終就會顯示子View的最終情況。那么,這個方法是怎么實現(xiàn)的呢?我們從源碼角度進(jìn)行解析。
View # requestLayout
//從源碼注釋可以看出,如果當(dāng)前View在請求布局的時候,View樹正在進(jìn)行布局流程的話,
//該請求會延遲到布局流程完成后或者繪制流程完成且下一次布局發(fā)現(xiàn)的時候再執(zhí)行。
@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;
}
//為當(dāng)前view設(shè)置標(biāo)記位 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方法中,首先先判斷當(dāng)前View樹是否正在布局流程,接著為當(dāng)前子View設(shè)置標(biāo)記位,該標(biāo)記位的作用就是標(biāo)記了當(dāng)前的View是需要進(jìn)行重新布局的,接著調(diào)用mParent.requestLayout方法,這個十分重要,因為這里是向父容器請求布局,即調(diào)用父容器的requestLayout方法,為父容器添加PFLAG_FORCE_LAYOUT標(biāo)記位,而父容器又會調(diào)用它的父容器的requestLayout方法,即requestLayout事件層層向上傳遞,直到DecorView,即根View,而根View又會傳遞給ViewRootImpl,也即是說子View的requestLayout事件,最終會被ViewRootImpl接收并得到處理??v觀這個向上傳遞的流程,其實是采用了責(zé)任鏈模式,即不斷向上傳遞該事件,直到找到能處理該事件的上級,在這里,只有ViewRootImpl能夠處理requestLayout事件。
ViewRootImpl # requestLayout:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
我們可以簡單的認(rèn)為mLayoutRequested為true會觸發(fā)performMeasure(內(nèi)部會調(diào)用onMeasure)和performLayout(內(nèi)部會調(diào)用onLayout)。然后在performDraw內(nèi)部draw的過程中發(fā)現(xiàn)mDirty為空,所以onDraw不會被調(diào)用,不重繪。
這么看來requestLayout不會導(dǎo)致onDraw調(diào)用了?
也不見得,我們知道requestLayout會導(dǎo)致performMeasure和performLayout,如果在layout過程中發(fā)現(xiàn)l,t,r,b和以前不一樣,那就會觸發(fā)一次invalidate。代碼在View的setFrame中,這個會在layout時被調(diào)用。
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有可能會導(dǎo)致onDraw被調(diào)用,也可能不導(dǎo)致onDraw被調(diào)用,取決于view的l,t,r,b是否改變。
一些知識點(diǎn):
- view不停找parent可以一直找到
DecorView,按理說DecorView是頂點(diǎn)了,但是DecorView還有個虛擬父view:ViewRootImpl。ViewRootImpl不是一個View或者ViewGroup,他有個成員mView是DecorView,所有的操作從ViewRootImpl開始自上而下分發(fā) - view的
invalidate不會導(dǎo)致ViewRootImpl的invalidate被調(diào)用,而是遞歸調(diào)用父view的invalidateChildInParent,直到ViewRootImpl的invalidateChildInParent,然后觸發(fā)peformTraversals,會導(dǎo)致當(dāng)前view被重繪,由于mLayoutRequested為false,不會導(dǎo)致onMeasure和onLayout被調(diào)用,而OnDraw會被調(diào)用 - 一個view的
invalidate會導(dǎo)致本身PFLAG_INVALIDATED置1,導(dǎo)致本身以及父族viewgroup的PFLAG_DRAWING_CACHE_VALID置0 -
requestLayout會直接遞歸調(diào)用父窗口的requestLayout,直到ViewRootImpl,然后觸發(fā)peformTraversals,由于mLayoutRequested為true,會導(dǎo)致onMeasure和onLayout被調(diào)用。不一定會觸發(fā)onDraw -
requestLayout觸發(fā)onDraw可能是因為在在layout過程中發(fā)現(xiàn)l,t,r,b和以前不一樣,那就會觸發(fā)一次invalidate,所以觸發(fā)了onDraw,也可能是因為別的原因?qū)е?code>mDirty非空(比如在跑動畫) -
requestLayout會導(dǎo)致自己以及父族view的PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED標(biāo)志被設(shè)置。
7.一般來說,只要刷新的時候就調(diào)用invalidate,需要重新measure就調(diào)用requestLayout,后面再跟個invalidate(為了保證重繪),個人理解。