Android invalidate

1.view.invalidate()

invalidate()方法用于重新触发一次View的绘制流程。

当调用View的invalidate方法后,View会不断向上调用父布局的绘制方法,并在这个过程中计算需要重绘的区域,最终调用过程会走到ViewRootImpl中,调用的是ViewRootImpl的performTraversals执行重绘操作。

View.java:

public void invalidate() {

    invalidate(true);

invalidateCache为true表示全部重绘。

public void invalidate(boolean invalidateCache) {

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

}

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

    if(mGhostView != null) {

        mGhostView.invalidate(true);

        return;

    }

    if(skipInvalidate()) {

        return;

    }

    ……

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

         //调用父类的invalidateChild方法

        p.invalidateChild(this, damage);

    }

    ...

}

在invalidateInternal()中判断当前view的状态,是否需要重绘,然后通过父view的invalidateChild方法,其中damage变量表示需要进行重绘的区域,后面在一系列的调用过程中会不断根据父布局来调整这个绘制区域。

ViewGroup中的invalidateChild方法:

@Override

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

    final AttachInfo attachInfo = mAttachInfo;

    ViewParent parent = this;      

    if (attachInfo != null) {

        //……省略了一些重新计算绘制区域的逻辑

        //这是一个从当前的布局View向上不断遍历当前布局View的父布局,最后遍历到ViewRootImpl的循环

        do {

            View view = null;

            if (parent instanceof View) {

                view = (View) parent;

            }

            if (drawAnimation) {

                if (view != null) {

                    view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

                } else if (parent instanceof ViewRootImpl) {

                    ((ViewRootImpl) parent).mIsAnimating = true;

                }

            }

            ...             

            //调用父布局的invalidateChildInParent方法

            parent = parent.invalidateChildInParent( location, dirty);

            ...

        } while (parent != null);

    }

}

然后看invalidateChildInParent方法:

@Override

public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {

    if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {

        if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) {

            //这里也是一些计算绘制区域的内容

        } else {

            mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;

            ...

            //这里也是一些计算绘制区域的内容

        }

        return mParent;

    }

    return null;

}

在ViewGroup的invalidateChild方法中有一个循环,循环里面一直调用父布局的invalidateChildInParent方法,而View和ViewGroup的最终父布局都是ViewRootImpl。

所以View中的invalidateInternal方法和ViewGroup中的invalidateChild方法最后殊途同归,都会调用到ViewRootImpl中的方法。

以后如果View没有父布局,那invalidateInternal方法就会调用ViewRootImpl的invalidateChild方法:

ViewRootImpl.java:

@Override

public void invalidateChild(View child, Rect dirty){

    invalidateChildInParent(null, dirty);

}

ViewGroup的invalidateChild方法最后会调用到这里:

 @Override

public ViewParent invalidateChildInParent(int[] location, Rect dirty) {

    checkThread();

    //如果dirty为null就表示要重绘当前ViewRootImpl指示的整个区域

    if (dirty == null) {

        invalidate();

        return null;

    } else if (dirty.isEmpty() && !mIsAnimating) {

        //如果dirty为empty则表示经过计算需要重绘的区域不需要绘制

        return null;

    }

    if (mCurScrollY != 0 || mTranslator != null) {

        mTempRect.set(dirty);

        dirty = mTempRect;

        if (mCurScrollY != 0) {

            dirty.offset(0, -mCurScrollY);

        }

        if (mTranslator != null) {

            mTranslator.translateRectInAppWin dowToScreen(dirty);

        }

        if (mAttachInfo.mScalingRequired) {

            dirty.inset(-1, -1);

        }

    }

    invalidateRectOnScreen(dirty);

    return null;

}

private void invalidateRectOnScreen(Rect dirty) {

    final Rect localDirty = mDirty;

    ……

    if (!mWillDrawSoon && (intersected || mIsAnimating)) {

        //调用scheduleTraversals方法进行绘制

        scheduleTraversals();

    }

}

可以看到在ViewRootImpl中最后都会调用scheduleTraversals方法进行绘制。

ViewRootImpl.java:

void scheduleTraversals() {

    if (!mTraversalScheduled) {

        mTraversalScheduled = true;

        mTraversalBarrier = mHandler.getLooper( ).getQueue().postSyncBarrier();

        //关键在这里!!!

        mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

        if (!mUnbufferedInputDispatch) {

            scheduleConsumeBatchedInput();

        }

        notifyRendererOfFramePending();

        pokeDrawLockIfNeeded();

    }

}

scheduleTraversals方法中调用了mChoreographer.postCallback方法。

Choreoprapher类的作用是编排输入事件、动画事件和绘制事件的执行,它的postCallback方法的作用就是将要执行的事件放入内部的一个队列中,最后会执行传入的Runnable,这里也就是mTraversalRunnable.

//ViewRootImpl.class

final class TraversalRunnable implements Runnable {

    @Override

    public void run() {

        doTraversal();

    }

}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

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

void doTraversal() {

    if (mTraversalScheduled) {

        mTraversalScheduled = false;

        mHandler.getLooper().getQueue().re moveSyncBarrier(mTraversalBarrier);

        if (mProfile) {

            Debug.startMethodTracing( "ViewAncestor");

        }

        performTraversals(); //找到了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,以下是流程图:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/124754197