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 ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
// Parent 父类 调用
p.invalidateChild(this, damage);
}
}
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);
}
invalidateChildInParent这个方法要么返回Parent父类,要么返回空
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
....
return mParent;
}
return null;
}
而do while 就相当于一直往上找父类.找到没有父类为止.如下图
而在最外面的view是ViewRootImpl.
找到ViewRootImpl的invalidateChildInParent方法.
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
......
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
......
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
......
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
ViewRootImpl的invalidateChildInParent方法里面有一个checkThread.有意思的方法.
这样可以引出另外一个问题.为什么不能在子线程更新UI.后面讲到.
//看到mTraversalRunnable 这样Runnable
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled)
//在UI的绘制流程中,这个方法是非常重要的
performTraversals();
}
}
private void performTraversals() {
// Ask host how big it wants to be
//测量,但是调用invalidate会导致不会走这个方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//摆放,但是调用invalidate会导致不会走这个方法
performLayout(lp, mWidth, mHeight);
//绘制.调用invalidate()只会调用performDraw()方法.所以重点在这里了.
performDraw();
}
private void draw(boolean fullRedrawNeeded) {
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty)
//这里的mView不是当前的view.是最外层的View
mView.draw(canvas);
}
最终调用View的draw方法.
public void draw(Canvas canvas) {
if (!dirtyOpaque) {
drawBackground(canvas);
}
//最终调用onDraw方法.
if (!dirtyOpaque) onDraw(canvas);
dispatchDraw(canvas);
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
总结:invalidate()流程.
一路往上跑,跑到最外层,调用draw(canvas) -> dispatchDraw(canvas) 画子孩子,一路往下画.最终画到当前调用invalidate()的View的onDrawa()方法.
invalidate()牵连着整个layout布局的View.
题外话:为什么不能在子线程更新UI?
开线程,更新UI 一般都会调用setText(),setImageView ,而setText,等都会调用到ViewRootImpl.checkThread()的方法.
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
这样方法用来检测线程的.如果不是在原始线程就会抛出异常.