整理一下invalidate()方法的执行过程,下面贴的代码都是只保留了需要留意的部分,
标注了要注意的地方,一目了然。
先总结一下,invalidate()就是使界面只进行重绘(draw)而不进行measure、layout过程。
View中:
public void invalidate() {
invalidate(true);
}
public 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) {
......
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); //这里
}
......
}
ViewGroup
public final void invalidateChild(View child, final Rect dirty) {
......
ViewParent parent = this;
......
do {
......
parent = parent.invalidateChildInParent(location, dirty); //这里
......
} while (parent != null);
}
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
......
return mParent; //这里
}
return null;
}
循着mParent一直调用到ViewRoot
ViewRootImpl
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;
}
......
invalidateRectOnScreen(dirty); //这里
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
......
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
下面注意mTraversalRunnable变量,看看是什么类型哪里赋值的
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //这里
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal(); //这里
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
是一个Runnable,注意run中调用的方法。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals(); //!这里!
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals()方法就是分法measure、layout、draw过程的地方,下面的代码比较长但是我们只需要留意invalidate()为什么只调用了draw而省略了测量布局过程。
注意invalidate()调用过程中没有设置mLayoutRequested,所以默认是false,进而方法里的局部变量insetsChanged也没有变true的机会了,然后导致后面if判断的时候无法进入measure和layout的执行流程。
private void performTraversals() {
......
boolean insetsChanged = false;
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
...... //这里有几个判断,可能会把instsChanged设置为true
}
......
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
......
if(...) {
if(...) {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
}
}
}
......
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, mWidth, mHeight);
......
}
......
if (!cancelDraw && !newSurface) {
......
performDraw();
}
......
}