requestlayout,onlayout,onDraw,invalidate区别与联系

invalidate

流程图:
流程图

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        }
11774
11775        if (skipInvalidate()) {
11776            return;
11777        }
11778
11779        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_INVALIDATED
11782                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
11783            if (fullInvalidate) {
11784                mLastIsOpaque = isOpaque();
11785                mPrivateFlags &= ~PFLAG_DRAWN;
11786            }
11787
11788            mPrivateFlags |= PFLAG_DIRTY;
11789
11790            if (invalidateCache) {
11791                mPrivateFlags |= PFLAG_INVALIDATED;
11792                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
11793            }
11794
11795            // 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            }
11803
11804            // 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            }
11811
11812            // 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;
4610
4611        final AttachInfo attachInfo = mAttachInfo;
4612        if (attachInfo != null) {
4613            // If the child is drawing an animation, we want to copy this flag onto
4614            // ourselves and the parent to make sure the invalidate request goes
4615            // through
4616            final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
4617                    == PFLAG_DRAW_ANIMATION;
4618
4619            // Check whether the child that requests the invalidate is fully opaque
4620            // Views being animated or transformed are not considered opaque because we may
4621            // 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 flag
4626            // Make sure we do not set both flags at the same time
4627            int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
4628
4629            if (child.mLayerType != LAYER_TYPE_NONE) {
4630                mPrivateFlags |= PFLAG_INVALIDATED;
4631                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
4632            }
4633
4634            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            }
4663
4664            do {
4665                View view = null;
4666                if (parent instanceof View) {
4667                    view = (View) parent;
4668                }
4669
4670                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                }
4677
4678                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4679                // flag coming from the child that initiated the invalidate
4680                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                }
4689
4690                parent = parent.invalidateChildInParent(location, dirty);
4691                if (view != null) {
4692                    // Account for transform on current parent
4693                    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 one
940         localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
941         // Intersect with the bounds of the window to skip
942         // updates that lie outside of the visible region
943         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绘制流程进行重绘任务。

requestLayout.

872     public void More ...requestLayout() {
873         if (!mHandlingLayoutInLayoutRequest) {
874             checkThread();
875             mLayoutRequested = true;
876             scheduleTraversals();
877         }
878     }

以上就是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时被调用。

requestLayout是否会导致全部View都触发Layout和measure?

一个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都不会被调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值