invalidate()源码分析

本文深入探讨了Android中View的invalidate()方法的工作流程,从触发重绘到绘制过程,涉及ViewParent、ViewGroup和ViewRootImpl的角色。讲解了invalidateChild()、invalidateChildInParent()和scheduleTraversals()等关键方法,以及如何最终调用doTraversal()进行布局测量、布局和绘制。整个流程展示了Android视图更新的底层机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

invalidate()方法的使用,就是重新触发一次View的绘制流程。

入口在View类中,

public void invalidate() {
    invalidate(true);
}

public void invalidate(boolean invalidateCache) {
    invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}

在 invalidateInternal(...) 中 ,调用

p.invalidateChild(this, damage); //p为ViewParent

ViewGroup继承View,实现了 ViewParent,在ViewGroup的invalidateChild()方法中有个do while 循环,循环中关键代码:

parent = parent.invalidateChildInParent(location, dirty);

ViewRootImpl实现了ViewParent,在ViewRootImpl中,

@Override
public void invalidateChild(View child, Rect dirty) {
    invalidateChildInParent(null, dirty);
}

@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    checkThread();
    if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);

    if (dirty == null) {
        invalidate();
        return null;
    } else if (dirty.isEmpty() && !mIsAnimating) {
        return null;
    }

    if (mCurScrollY != 0 || mTranslator != null) {
        mTempRect.set(dirty);
        dirty = mTempRect;
        if (mCurScrollY != 0) {
            dirty.offset(0, -mCurScrollY);
        }
        if (mTranslator != null) {
            mTranslator.translateRectInAppWindowToScreen(dirty);
        }
        if (mAttachInfo.mScalingRequired) {
            dirty.inset(-1, -1);
        }
    }

    invalidateRectOnScreen(dirty);

    return null;
}

private void invalidateRectOnScreen(Rect dirty) {
    final Rect localDirty = mDirty;
    if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
        mAttachInfo.mSetIgnoreDirtyState = true;
        mAttachInfo.mIgnoreDirtyState = true;
    }

    // Add the new dirty rect to the current one
    localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
    // Intersect with the bounds of the window to skip
    // updates that lie outside of the visible region
    final float appScale = mAttachInfo.mApplicationScale;
    final boolean intersected = localDirty.intersect(0, 0,
            (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
    if (!intersected) {
        localDirty.setEmpty();
    }
    if (!mWillDrawSoon && (intersected || mIsAnimating)) {
        scheduleTraversals();
    }
}

invalidateChild(..)方法调用invalidateChildInParent(..),

invalidateChildInParent(..)方法调用invalidateRectOnScreen(dirty),

invalidateRectOnScreen(dirty)方法中最终会调用scheduleTraversals()

scheduleTraversals()这个方法很关键,会通过一系列的调用最终调用TraversalRunnable.run()方法,然后调用doTraversal()方法,doTraversal()中调用performTraversals()。

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

performTraversals()主要做三件事:

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

performLayout(lp, mWidth, mHeight);

performDraw();

最后会调用View的绘制方法:

measure(childWidthMeasureSpec, childHeightMeasureSpec);

layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

draw(fullRedrawNeeded);

至此,整个invalidate()流程结束,贯穿着一个布局或View的所有子view,以下是流程图:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值