1.view.invalidate()
invalidate是废弃、使无效的意思。Android中需要重绘某个视图时就可以调用该函数,表示view的某个显示区域内容变脏了,该显示区域需要被重新绘制。
view.invalidate()方法用来完成UI的刷新,它可以重新触发一次View的绘制流程。绘制过程一般不会对所有视图进行重绘,而仅绘制那些“需要重绘”的视图,也就是mPrivateFlags中包含标志位为DRAWN的视图(当某视图需要绘制时,就给它的mPrivateFlags中添加该标志位)。
函数invalidate()会根据所有视图中的DRAWN标志位计算具体哪个区域需要重绘,这个区域将用一个矩形Rect表示,并最终将这个Rect存放到ViewRoot中的mDirty变量中,之后的重绘过程将重绘所有包含在该mDirty区域中的视图。
注意: invalidate方法必须在主线程中才能调用。如果需要在非UI线程中使用,可以调用view.postInvalidate() 方法。
invalidate方法和postInvalidate方法都用于View的刷新,invalidate方法应用在UI线程中,而postInvalidate方法应用在非UI线程中,用于将线程切换到UI线程,postInvalidate方法最后调用的也是invalidate方法。
invalidate()方法用来刷新重绘当前的View,如果当前View的布局尺寸、位置没有变化,仅仅是绘制内容变化了,那么就可以调用invalidate()方法。
2.invalidate刷新流程
Android中View是以树形结构组织的,如果红色的View调用invalidate(),看一下整个流程:
某个view调用invalidate()后,如果这个View可见,那么会使整个View视图无效,然后在未来的某个时间点View的onDraw()方法将被调用。
①View.invalidate()
View.class:
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);//传入当前view的位置参数
}
invalidateInternal方法的参数:
l,t,r,b是View的大小;
invalidateCache: 设置View的缓存是否失效,通常情况下是ture,当View的大小改变时为false;
fullInvalidate: 默认为 true。
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
if(skipInvalidate()) { //判断该子View是否可见或者是否处于动画中。如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘
return;
}
……
// 根据View的标记位判断该子View是否需要重绘,假如View没有任何变化就不需要重绘
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) { //默认为true
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN; //清除绘制标记
}
// 需要绘制,设置PFLAG_DIRTY标记位
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent; //子View的ViewParent就是它的父View,即ViewGroup
if (p != null && ai != null && l < r && t < b) {
// 记录需要重新绘制的区域damge,该区域为该View的尺寸
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
//p为该view的父布局,调用父布局的invalidateChild()
p.invalidateChild(this, damage);
}
...
}
}
首先判断该子View是否可见或者是否处于动画中,如果子View不可见或者没有处于动画中,则不让该子View失效,即该子View不会被重绘。
然后根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘。
最后调用父类ViewParent的invalidateChild()方法,子View的ViewParent就是它的父View即ViewGroup。将View需要绘制大小Rect告诉父ViewGroup,并调用父ViewGroup的invalidateChild(),其中damage变量表示需要进行重绘的区域,后面在一系列的调用过程中会不断根据父布局来调整这个绘制区域。
②ViewGroup.invalidateChild()
ViewGroup.class:
public final void invalidateChild(View child, final Rect dirty) {
// 如果是硬件加速,走该分支
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null && attachInfo.mHardwareAccelerated) {
onDescendantInvalidated(child, child);
return;
}
// 软件绘制
ViewParent parent = this; //parent为当前的ViewGroup
if (attachInfo != null) {
...
//从当前View向上不断遍历当前View的父布局,最后遍历到ViewRootImpl
do {
...
//在do while循环中为每个父布局设置标记位
if (view != null) { //父视图不为空
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; //设置父类的mPrivateFlags标记位
}
}
parent = parent.invalidateChildInParent( location, dirty); //在do while中循环调用父布局的invalidateChildInParent方法
//在do while中循环为每个父布局设置dirty区域
if(view != null) {
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.floor(boundin