流程圖: 
如上面源碼所示,View的invalidate會調到invalidateInternal,里面會設置2個標志位PFLAG_INVALIDATED和PFLAG_DRAWING_CACHE_VALID,PFLAG_INVALIDATED置為1,PFLAG_DRAWING_CACHE_VALID置為0. 并且11801行調用invalidateChild()。
ViewGroup中的invalidateChild方法:
public final void More ...invalidateChild(View child, final Rect dirty) {4609 ViewParent parent = this;46104611 final AttachInfo attachInfo = mAttachInfo;4612 if (attachInfo != null) {4613 // If the child is drawing an animation, we want to copy this flag onto4614 // ourselves and the parent to make sure the invalidate request goes4615 // through4616 final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)4617 == PFLAG_DRAW_ANIMATION;46184619 // Check whether the child that requests the invalidate is fully opaque4620 // Views being animated or transformed are not considered opaque because we may4621 // be invalidating their old position and need the parent to paint behind them.4622 Matrix childMatrix = child.getMatrix();4623 final boolean isOpaque = child.isOpaque() && !drawAnimation &&4624 child.getAnimation() == null && childMatrix.isIdentity();4625 // Mark the child as dirty, using the appropriate flag4626 // Make sure we do not set both flags at the same time4627 int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;46284629 if (child.mLayerType != LAYER_TYPE_NONE) {4630 mPrivateFlags |= PFLAG_INVALIDATED;4631 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;4632 }46334634 final int[] location = attachInfo.mInvalidateChildLocation;4635 location[CHILD_LEFT_INDEX] = child.mLeft;4636 location[CHILD_TOP_INDEX] = child.mTop;4637 if (!childMatrix.isIdentity() ||4638 (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {4639 RectF boundingRect = attachInfo.mTmpTransformRect;4640 boundingRect.set(dirty);4641 Matrix transformMatrix;4642 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {4643 Transformation t = attachInfo.mTmpTransformation;4644 boolean transformed = getChildStaticTransformation(child, t);4645 if (transformed) {4646 transformMatrix = attachInfo.mTmpMatrix;4647 transformMatrix.set(t.getMatrix());4648 if (!childMatrix.isIdentity()) {4649 transformMatrix.preConcat(childMatrix);4650 }4651 } else {4652 transformMatrix = childMatrix;4653 }4654 } else {4655 transformMatrix = childMatrix;4656 }4657 transformMatrix.mapRect(boundingRect);4658 dirty.set((int) (boundingRect.left - 0.5f),4659 (int) (boundingRect.top - 0.5f),4660 (int) (boundingRect.right + 0.5f),4661 (int) (boundingRect.bottom + 0.5f));4662 }46634664 do {4665 View view = null;4666 if (parent instanceof View) {4667 view = (View) parent;4668 }46694670 if (drawAnimation) {4671 if (view != null) {4672 view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;4673 } else if (parent instanceof ViewRootImpl) {4674 ((ViewRootImpl) parent).mIsAnimating = true;4675 }4676 }46774678 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque4679 // flag coming from the child that initiated the invalidate4680 if (view != null) {4681 if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&4682 view.getSolidColor() == 0) {4683 opaqueFlag = PFLAG_DIRTY;4684 }4685 if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {4686 view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;4687 }4688 }46894690 parent = parent.invalidateChildInParent(location, dirty);4691 if (view != null) {4692 // Account for transform on current parent4693 Matrix m = view.getMatrix();4694 if (!m.isIdentity()) {4695 RectF boundingRect = attachInfo.mTmpTransformRect;4696 boundingRect.set(dirty);4697 m.mapRect(boundingRect);4698 dirty.set((int) (boundingRect.left - 0.5f),4699 (int) (boundingRect.top - 0.5f),4700 (int) (boundingRect.right + 0.5f),4701 (int) (boundingRect.bottom + 0.5f));4702 }4703 }4704 } while (parent != null);4705 }4706 }可以看到invalidateChild內部有個dowhile循環,不停調用父view的invalidateChildInParent,一直到調用ViewRootImpl的invalidateChildInParent。我們看invalidateChild的L21,把PFLAG_DRAWING_CACHE_VALID置為0,在dowhile循環后,當前view的所有父view,父view的父view。。。都會被PFLAG_DRAWING_CACHE_VALID置為0.調用invalidateChildInParent會傳進去一個Rect叫dirty,代表子窗口需要刷新的rect,父窗口會根據這個rect和父窗口本身做union,從而得到父窗口需要刷新的rect區域,然后再傳給父窗口的父窗口,一直遞歸直到ViewRootImpl。
908 public ViewParent More ...invalidateChildInParent(int[] location, Rect dirty) {909 checkThread();910 if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);911 912 if (dirty == null) {913 invalidate();914 return null;915 } else if (dirty.isEmpty() && !mIsAnimating) {916 return null;917 }918 919 if (mCurScrollY != 0 || mTranslator != null) {920 mTempRect.set(dirty);921 dirty = mTempRect;922 if (mCurScrollY != 0) {923 dirty.offset(0, -mCurScrollY);924 }925 if (mTranslator != null) {926 mTranslator.translateRectInAppWindowToScreen(dirty);927 }928 if (mAttachInfo.mScalingRequired) {929 dirty.inset(-1, -1);930 }931 }932 933 final Rect localDirty = mDirty;934 if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {935 mAttachInfo.mSetIgnoreDirtyState = true;936 mAttachInfo.mIgnoreDirtyState = true;937 }938 939 // Add the new dirty rect to the current one940 localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);941 // Intersect with the bounds of the window to skip942 // updates that lie outside of the visible region943 final float appScale = mAttachInfo.mapplicationScale;944 final boolean intersected = localDirty.intersect(0, 0,945 (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));946 if (!intersected) {947 localDirty.setEmpty();948 }949 if (!mWillDrawSoon && (intersected || mIsAnimating)) {950 scheduleTraversals();951 }952 953 return null;954 }再來看ViewRootImpl的invalidateChildInParent,這個ViewRootImpl類的invalidateChildInParent方法直接返回了null,也就是上面ViewGroup中說的,層層上級傳遞到ViewRootImpl的invalidateChildInParent方法結束了那個do while循環。這里重點是調用scheduleTraversals,scheduleTraversals會通過Handler的Runnable發送一個異步消息,調用doTraversal方法,然后最終調用performTraversals()執行重繪。performTraversals就是整個View數開始繪制的起始調用地方,所以說View調運invalidate方法的實質是層層上傳到父級,直到傳遞到ViewRootImpl后觸發了scheduleTraversals方法,然后整個View樹開始重新按照View繪制流程進行重繪任務。
以上就是ViewRootImpl的requestLayout方法,可以看到mLayoutRequested變true了,然后觸發了scheduleTraversals 方法,requestLayout與invalidate的調用過程類似,只是設置的標志位不同,導致View的繪制流程中執行方法不同而已。
我們可以簡單的認為mLayoutRequested為true會觸發perfomMeasure(內部會調用onMeasure)和performLayout(內部會調用onLayout)。然后在performDraw內部draw的過程中發現mDirty為空,所以onDraw不會被調用,不重繪。 這么看來requestLayout不會導致onDraw調用了?
也不見得,我們知道requestLayout會導致perfomMeasure和performLayout,如果在layout過程中發現l,t,r,b和以前不一樣,那就會觸發一次invalidate。代碼在View的setFrame中,這個會在layout時被調用。
一個view調用了requestLayout,那么他自己與他的父族view都會被設置為PFLAG_FORCE_LAYOUT,我們在看看measure過程,measure的核心代碼處必須滿足3個條件之一,而他自己與他的父族view都會被設置為PFLAG_FORCE_LAYOUT,所以他們都必然會被重新measure,但是其他的view就不一定了,就看這3個條件是否會滿足。 結論是requestLayout會導致部分view重新measure和layout。a的requestLayout必然會導致a,ap…的重新measure,ap的requestLayout必定會導致ap的measure,但不一定會導致a的measure。 tip: 因為onMeasure后會設置了PFLAG_LAYOUT_REQUIRED標志位,這會導致調用onLayout。
requestLayout()方法會調用measure過程和layout過程,不會調用draw過程,也不會重新繪制任何View包括該調用者本身 invalidate系列方法請求重繪View樹(也就是draw方法),在performTraversals方法中,mLayoutRequested為false,所有onMeasure和onLayout都不會被調用。
新聞熱點
疑難解答