Android View更新和重绘之requestLayout、invalidate与postInvalidate

前言增加一个TextView显示引发的血案…. 首先简单来介绍下页面的布局: 描述: -> 父RelativeLayout ->-> 子自定义View(A) ->-> 子View 。。。 ->-> (某一天某小伙伴增加了一个TextView) -> 父RelativeL...
摘要由CSDN通过智能技术生成

前言

增加一个TextView显示引发的血案….
首先简单来介绍下页面的布局:

描述:
-> 父RelativeLayout
->-> 子自定义View(A)
->-> 子View
。。。
->-> (某一天某小伙伴增加了一个TextView)
-> 父RelativeLayout

小伙伴增加的TextView会不定时更新内容,小伙伴想,这么简单,写完收工!
但是,问题发生了,自定义View A出现了问题,小伙伴急招同事来帮忙。

1、发现问题

在工作中遇到问题,自己搞不定能迅速找到一个人搞定,也是工作能力哈!
同事过来,发现自定义的View A调用invalidate方法时会再次执行onMeasure方法,而onMeasure方法中定义了一些初始化的操作。为什么View A会不定时的执行onMeasure方法?原来是小伙伴增加的TextView会不定时更新内容,To farce a view to draw,call invalidate()—摘自View类源码。
从上面这句话看出,invalidate方法会执行draw过程,重绘View树。在同一个父View(即RelativeLayout中),一个View刷新数据,会影响其他View。至此,根本原因已经找到。

1、Invalidate View更新和重绘

To farce a view to draw,call invalidate().——摘自View类源码
从上面这句话看出,invalidate方法会执行draw过程,重绘View树。

引申:当View的appearance发生改变,比如状态改变(enable,focus),背景改变,隐显改变等,这些都属于appearance范畴,都会引起invalidate操作。
所以当我们改变了View的appearance,需要更新界面显示,就可以直接调用invalidate方法。

View(非容器类)调用invalidate方法只会重绘自身,ViewGroup调用则会重绘整个View树。

首先,一个子View调用该方法,那么我们直接看View#invalidate方法:

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;
    }

    //根据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) {
            mLastIsOpaque = isOpaque();
            mPrivateFlags &= ~PFLAG_DRAWN;
        }

        //设置PFLAG_DIRTY标记位
        mPrivateFlags |= PFLAG_DIRTY;

        if (invalidateCache) {
            mPrivateFlags |= PFLAG_INVALIDATED;
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
        }

        // Propagate the damage rectangle to the parent view.
        //把需要重绘的区域传递给父容器
        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);
            //调用父容器的方法,向上传递事件
            p.invalidateChild(this, damage);
        }
        ...
    }
}

可以看出,invalidate有多个重载方法,但最终都会调用invalidateInternal方法,在这个方法内部,进行了一系列的判断,判断View是否需要重绘,接着为该View设置标记位,然后把需要重绘的区域传递给父容器,即调用父容器的invalidateChild方法。
接着我们看ViewGroup#invalidateChild

/**
 * Don't call or override this method. It is used for the implementation of
 * the view hierarchy.
 */
public final void invalidateChild(View child, final Rect dirty) {

    //设置 parent 等于自身
    ViewParent parent = this;

    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        // If the child is drawing an animation, we want to copy this flag onto
        // ourselves and the parent to make sure the invalidate request goes
        // through
        final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
                == PFLAG_DRAW_ANIMATION;

        // Check whether the child that requests the invalidate is f
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值