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,以下是流程图: