Android invalidate、requestLayout

1.view.invalidate()

invalidate是废弃、使无效的意思。Android中需要重绘某个视图时就可以调用该函数,表示view的某个显示区域内容变脏了,该显示区域需要被重新绘制。

view.invalidate()方法用来完成UI的刷新,它可以重新触发一次View的绘制流程。绘制过程一般不会对所有视图进行重绘,而仅绘制那些“需要重绘”的视图,也就是mPrivateFlags中包含标志位为DRAWN的视图(当某视图需要绘制时,就给它的mPrivateFlags中添加该标志位)。

函数invalidate()会根据所有视图中的DRAWN标志位计算具体哪个区域需要重绘,这个区域将用一个矩形Rect表示,并最终将这个Rect存放到ViewRoot中的mDirty变量中,之后的重绘过程将重绘所有包含在该mDirty区域中的视图。

注意: invalidate方法必须在主线程中才能调用。如果需要在非UI线程中使用,可以调用view.postInvalidate() 方法。

invalidate方法和postInvalidate方法都用于View的刷新,invalidate方法应用在UI线程中,而postInvalidate方法应用在非UI线程中,用于将线程切换到UI线程,postInvalidate方法最后调用的也是invalidate方法。

invalidate()方法用来刷新重绘当前的View,如果当前View的布局尺寸、位置没有变化,仅仅是绘制内容变化了,那么就可以调用invalidate()方法。

 

2.invalidate刷新流程

Android中View是以树形结构组织的,如果红色的View调用invalidate(),看一下整个流程:

d2a48b5b20a149fda6a16d5256f585e9.png

某个view调用invalidate()后,如果这个View可见,那么会使整个View视图无效,然后在未来的某个时间点View的onDraw()方法将被调用。

①View.invalidate()

View.class:

public void invalidate() {

    invalidate(true);

}

public void invalidate(boolean invalidateCache) {

    invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);//传入当前view的位置参数

}

invalidateInternal方法的参数:

l,t,r,b是View的大小;

invalidateCache: 设置View的缓存是否失效,通常情况下是ture,当View的大小改变时为false;

fullInvalidate: 默认为 true。

void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {

    if(skipInvalidate()) { //判断该子View是否可见或者是否处于动画中。如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘       

        return;

    }

    ……

    // 根据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) { //默认为true

            mLastIsOpaque = isOpaque();

            mPrivateFlags &= ~PFLAG_DRAWN; //清除绘制标记

        }

       // 需要绘制,设置PFLAG_DIRTY标记位

        mPrivateFlags |= PFLAG_DIRTY;

        if (invalidateCache) {

            mPrivateFlags |= PFLAG_INVALIDATED;

            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;

        }

        final AttachInfo ai = mAttachInfo;

        final ViewParent p = mParent; //子View的ViewParent就是它的父View,即ViewGroup

        if (p != null && ai != null && l < r && t < b) {

            // 记录需要重新绘制的区域damge,该区域为该View的尺寸

            final Rect damage = ai.mTmpInvalRect;

            damage.set(l, t, r, b);

            //p为该view的父布局,调用父布局的invalidateChild()

            p.invalidateChild(this, damage);

        }

       ...

    }

}

首先判断该子View是否可见或者是否处于动画中,如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘。

然后根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘。

最后调用父类ViewParent的invalidateChild()方法,子View的ViewParent就是它的父View即ViewGroup。将View需要绘制大小Rect告诉父ViewGroup,并调用父ViewGroup的invalidateChild(),其中damage变量表示需要进行重绘的区域,后面在一系列的调用过程中会不断根据父布局来调整这个绘制区域。

②ViewGroup.invalidateChild()

ViewGroup.class:

public final void invalidateChild(View child, final Rect dirty) {

    // 如果是硬件加速,走该分支

    final AttachInfo attachInfo = mAttachInfo;

    if (attachInfo != null && attachInfo.mHardwareAccelerated) {

        onDescendantInvalidated(child, child);

        return;

    }

    // 软件绘制

    ViewParent parent = this; //parent为当前的ViewGroup

    if (attachInfo != null) {

        ...

        //从当前View向上不断遍历当前View的父布局,最后遍历到ViewRootImpl

        do {

            ...

         //在do while循环中为每个父布局设置标记位

            if (view != null) { //父视图不为空

                if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {

                    view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; //设置父类的mPrivateFlags标记位

                }

           }

           parent = parent.invalidateChildInParent( location, dirty); //在do while中循环调用父布局的invalidateChildInParent方法

     //在do while中循环为每个父布局设置dirty区域

           if(view != null) {

                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.floor(boundin

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值