View的invalidate()方法源码分析

2021-07-08  本文已影响0人  碧云天EthanLee

先点进源码,点击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() 方法。

上一篇下一篇

猜你喜欢

热点阅读