先点进源码,点击invalidate() 方法查看调用路径:
invalidate() --> invalidate(boolean invalidateCache) --> invalidateInternal()
--> p.invalidateChild(this, damage);
可以看到最终会调用到 p.invalidateChild(this, damage) 这行关键代码。其中 p就是
当前 View的父布局。
下面再看该父布局 ViewGroup 的 invalidateChild 方法里的一段关键代码:
ViewGroup.java --> invalidateChild
do {
View view = null;
...
...
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
}
}
parent = parent.invalidateChildInParent(location, dirty); // 注释 1
...
...
} while (parent != null);
可以看到 ViewGroup 的 invalidateChild 方法里使用了一个 do ~ while 循环。如上注释 1 处,不断地向上调用父布局的 invalidateChildInParent 方法。直到获取到的父布局(parent )为空为止。最终会调用到根布局。
现在看看源码根布局实现类 ViewRootImpl.java的 invalidateChildInParent ()方法(该方法第一行就调用了更新UI前的线程检查方法checkThread();)。
再往下走调用路径:
invalidateChildInParent - -> invalidateRectOnScreen(dirty); -->
scheduleTraversals() --> mTraversalRunnable(变量) -->
点进 TraversalRunnable 内部类里 -- > doTraversal() --> performTraversals();
到这里路径就走得差不多了,performTraversals(); 就是 View的绘制流程里比较重要的方法。在这个方法里可以找到调用的下面3个方法:
ViewRootImpl.java --> performTraversals()
...
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw(); // 注释 2
可以看到,上面 performTraversals() 方法里调用了绘制流程中的测量、布局和绘制的三个方法。当然,在调用View 的invalidate 方法时,一般不会触发测量和布局。所以,调用 View 的invalidate 方法进行重绘时,最终会层层调用到根布局的 performDraw() 方法,也就是上面代码注释 2 处。然后根布局就会自上而下地调用子布局或子View的 draw() 方法,最终完成绘制。
performDraw() 再往后的调用路径:
performDraw() --> draw(fullRedrawNeeded) --> drawSoftware() -->
mView.draw(canvas);
这样就走完了,上面路径的最后一步的 mView 变量是当前布局的子布局。所以draw方法会层层调用到我们最初调用 invalidate() 方法的子 View 的 draw() 方法。
网友评论