流程图:
11764 void More ...invalidate(boolean invalidateCache) {11765 invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);11766 }void More ...invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,11769 boolean fullInvalidate) {11770 if (mGhostView != null) {11771 mGhostView.invalidate(true);11772 return;11773 }1177411775 if (skipInvalidate()) {11776 return;11777 }1177811779 if ((mPRivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)11780 || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)11781 || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED11782 || (fullInvalidate && isOpaque() != mLastIsOpaque)) {11783 if (fullInvalidate) {11784 mLastIsOpaque = isOpaque();11785 mPrivateFlags &= ~PFLAG_DRAWN;11786 }1178711788 mPrivateFlags |= PFLAG_DIRTY;1178911790 if (invalidateCache) {11791 mPrivateFlags |= PFLAG_INVALIDATED;11792 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;11793 }1179411795 // Propagate the damage rectangle to the parent view.11796 final AttachInfo ai = mAttachInfo;11797 final ViewParent p = mParent;11798 if (p != null && ai != null && l < r && t < b) {11799 final Rect damage = ai.mTmpInvalRect;11800 damage.set(l, t, r, b);11801 p.invalidateChild(this, damage);11802 }1180311804 // Damage the entire projection receiver, if necessary.11805 if (mBackground != null && mBackground.isProjected()) {11806 final View receiver = getProjectionReceiver();11807 if (receiver != null) {11808 receiver.damageInParent();11809 }11810 }1181111812 // Damage the entire IsolatedZVolume receiving this view's shadow.11813 if (isHardwareAccelerated() && getZ() != 0) {11814 damageShadowReceiver();11815 }11816 }11817 }如上面源码所示,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都不会被调用。
新闻热点
疑难解答