源码分析:知道原理为先,别一开始就往深处走, 要循序渐进
invalidate()源码
代码语言:javascript复制 public void invalidate() {
invalidate(true);
}
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* @param invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View's contents or
* dimensions have not changed.
* @hide
*/
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) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) {
return;
}
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
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);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
}
}
invalidateInternal()
- 首先调用了**
invalidateInternal()
**;
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate)
invalidateInternal()
**中的**skipInvalidate()
**肯定看得懂,** 即符合某几个条件 【 如界面不可见了(**(...) != VISIBLE
**)、 或者没有设置动画**mCurrentAnimation == null
**等 (**mCurrentAnimation
**在View源码是一个全局变量,跟动画有关,可以看一下源码) 】, 则跳过**invalidate()
**的调用 【直接**return
**了,更不用说会调用**onDraw()
**了】:
//invalidateInternal() 中的 skipInvalidate()
if (skipInvalidate()) {
return;
}
...
//skipInvalidate() 源码
/**
* Do not invalidate views which are not visible and which are not running an animation. They
* will not get drawn and they should not set dirty flags as if they will be drawn
*/
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
- 往下继续看**
invalidateInternal()
**的代码,可以翻到: 这里调用了**ViewParent
**的**invalidateChild()
**:
ViewParent
**自然是往**ViewGroup
**的源码看,**
这里有个parent实例的赋值,直接赋值this,
接着主要看这里有个**do...while()
**循环:
!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!
这个**do...while()
**循环的目的就是不断地拿这个**parent
**实例,
去进行一系列的操作;
其中每一次循环,
又拿这个**parent
**实例去调用**invalidateChildInParent()
**,
去获取这个**parent
**实例的**parent
**实例:
!!!!!!!!!!!!!!!!
如此不断循环,
最终会调用到**最外层
**的**ViewGroup【View】
**的**invalidateChildInParent()
**;
!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!
代码语言:javascript复制 parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
invalidateChildInParent()
**源码:【会返回一个**ViewParent
**实例或者空;】**
@Deprecated
@Override
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
// either DRAWN, or DRAWING_CACHE_VALID
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
...
}
final int left = mLeft;
final int top = mTop;
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
dirty.setEmpty();
}
}
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
} else {
...
return mParent;
}
return null;
}
接着这部分要在本地的SDK目录中找到这个**ViewRootImpl
**源码文件,
然后把它拖到AS中阅读:
接着是:
其中最后调用了**scheduleTraversals();
**
接着往下点,
接着往下点,
!!!!!!!!!!!!!!!!!!!!!
好了,重点就是这个**performTraversals()
**方法,它在绘制流程中非常重要。
【一个近千行的方法】
!!!!!!!!!!!!!!!!!!!!!
其中又调用了三个重要的方法:
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw()
!!!!!!!!!!!!!!!!!
setContentView()
**的流程中,调用了以上三个方法,**
invalidate()
**主要是调用了**performDraw()
**方法,**
由于源码中存在条件判断,所以是不会进**performMeasure()
**、**performLayout()
**两个方法的;
!!!!!!!!!!!!!!!!!
所以下面主要看**performDraw()
**方法,
performDraw()
**中有一个draw方法,**
private boolean draw(boolean fullRedrawNeeded)
其中又调用了**drawSoftware()
**,
drawSoftware()
**中,出现了**Canvas
**和**mSurface
**,**
mSurface
**锁定了Canvas【**lockCanvas()
**】;大概了解一下就好;**
接着呢,又调用了**View的draw()
**方法:
View的draw()
**的源码**
我们在《自定义View | 基础概述 & 自定义TextView实战 & 基于源码分析自定义View继承自ViewGroup时无法正常绘制的问题》这篇笔记中有提及到,
下面是源码注释中总结的六个步骤:
小拓展——子线程中为什么不能更新UI? 源码角度稍微分析一下: 调用setText()、setImageView()等方法, 最后都会调到**
ViewRootImpl
**的**checkThread()
**;checkThread()
**是用来检测线程的;**
Thread.currentThread()
**是子线程,**
mThread
**是在构造函数中初始化的,**
这里的是主线程;
归结一下invalidate()的流程
简要浏览完源码了,归结一下**invalidate()
**的大概流程:
首先从调用了**invalidate()
**的**View
**开始,
一路往上,跑到最外层的ViewRoot:
用最外层的View调用**draw()
**,
draw()
**如源码第四步有一个**dispatchDraw()
**,又会一路往下画,**
不断的绘制**子孩子
**,再绘制**子孩子
**的**子孩子
**,
最终绘制到 调用了**invalidate()
**的**View
**的 onDraw()
**方法;**
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
所以当我们简简单单地调用一个View的**invalidate()
**时,
它牵连的是**整个布局中
**的**View实例
**,
而不仅仅是 当前调用**invalidate()
**的**View实例
**一个 而已;
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!