invalidate():
invalidate()函数的主要作用是请求View树进行重绘,该函数可以由应用程序调用,或者由系统函数间接调用,例如setEnable(), setSelected(), setVisiblity()都会间接调用到invalidate()来请求View树重绘,更新View树的显示。
下面通过源码来了解invalidate()函数的工作原理,首先来看View类中invalidate()的实现过程:
1. public void invalidate() {
2. invalidate(true);
3. }
invalidate()函数会转而调用invalidate(true)
1. void invalidate(boolean invalidateCache) {
2. if (ViewDebug.TRACE_HIERARCHY) {
3. ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
4. }
5.
6. if (skipInvalidate()) {
7. return;
8. }
9. if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
10. (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||
11. (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {
12. mLastIsOpaque = isOpaque();
13. mPrivateFlags &= ~DRAWN;
14. mPrivateFlags |= DIRTY;
15. if (invalidateCache) {
16. mPrivateFlags |= INVALIDATED;
17. mPrivateFlags &= ~DRAWING_CACHE_VALID;
18. }
19. final AttachInfo ai = mAttachInfo;
20. final ViewParent p = mParent;
21. //noinspection PointlessBooleanExpression,ConstantConditions
22. if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
23. if (p != null && ai != null && ai.mHardwareAccelerated) {
24. // fast-track for GL-enabled applications; just invalidate the whole hierarchy
25. // with a null dirty rect, which tells the ViewAncestor to redraw everything
26. p.invalidateChild(this, null);
27. return;
28. }
29. }
30.
31. if (p != null && ai != null) {
32. final Rect r = ai.mTmpInvalRect;
33. r.set(0, 0, mRight - mLeft, mBottom - mTop);
34. // Don't call invalidate -- we don't want to internally scroll
35. // our own bounds
36. p.invalidateChild(this, r);
37. }
38. }
39. }
具体分析:
1、首先调用skipInvalidate(),该函数主要判断该View是否不需要重绘,如果不许要重绘则直接返回,不需要重绘的条件是该View不可见并且未进行动画
2、接下来的if语句是来进一步判断View是否需要绘制,其中表达式 (mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)的意思指的是如果View需要重绘并且其大小不为0,其余几个本人也未完全理解,还望高手指点~~如果需要重绘,则处理相关标志位
3、对于开启硬件加速的应用程序,则调用父视图的invalidateChild函数绘制整个区域,否则只绘制dirty区域(r变量所指的区域),这是一个向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新Rect做交集。
我们主要关注dirty区域不是null(非硬件加速)的情况
1、判断子视图是否是不透明的(不透明的条件是isOpaque()返回true,视图未进行动画以及child.getAnimation() == null),并将判断结果保存到变量isOpaque中,如果不透明则将变量opaqueFlag设置为DIRTY_OPAQUE,否则设置为DIRTY。
2、定义location保存子视图的左上角坐标
3、如果子视图正在动画,那么父视图也要添加动画标志,如果父视图是ViewGroup,那么给mPrivateFlags添加DRAW_ANIMATION标识,如果父视图是ViewRoot,则给其内部变量mIsAnimating赋值为true
4、设置dirty标识,如果子视图是不透明的,则父视图设置为DIRTY_OPAQUE,否则设置为DIRTY
5、调用parent.invalidateChildInparent(),这里的parent有可能是ViewGroup,也有可能是ViewRoot(最后一次while循环),首先来看ViewGroup, ViewGroup中该函数的主要作用是对dirty区域进行计算
具体分析如下:
1、判断此次调用是否在UI线程中进行
2、将dirty的坐标位置转换为ViewRoot的屏幕显示区域
3、更新mDirty变量,并调用scheduleTraversals发起重绘请求
分析postInvalidate():
postInvalidate()方法相对就在View源码中就比较少,
三者区别:
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:
Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能
看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。
Android 程序默认情况下也只有一个进程,但一个进程下却可以有许多个线程。在这么多线程当中,把主要是负责控制UI界面的显示、更新和控件交互的线程称为UI线程,由于onCreate()方法是由UI线程执行的,所以也可以把UI线程理解为主线程。其余的线程可以理解为工作者线程。invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新。而postInvalidate()在工作者线程中被调用。
一、利用invalidate()刷新界面
实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
// 在onCreate()中开启线程
new Thread(new GameThread()).start();、
// 实例化一个handler
Handler myHandler = new Handler() {
// 接收到消息后处理
public void handleMessage(Message msg) {
switch (msg.what) {
case Activity01.REFRESH:
mGameView.invalidate(); // 刷新界面
break;
}
super.handleMessage(msg);
}
};
class GameThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = Activity01.REFRESH;
// 发送消息
Activity01.this.myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
二、使用postInvalidate()刷新界面
使用postInvalidate则比较简单,不需要handler,直接在工作线程中调用postInvalidate即可。
class GameThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 使用postInvalidate可以直接在工作线程中更新界面
mGameView.postInvalidate();
}
}
}
postInvalidateOnAnimation()
1. public void postInvalidateOnAnimation() {
2. // We try only with the AttachInfo because there's no point in invalidating
3. // if we are not attached to our window
4. final AttachInfo attachInfo = mAttachInfo;
5. if (attachInfo != null) {
6. attachInfo.mViewRootImpl.dispatchInvalidateOnAnimation(this);
7. }
8. }
ViewRootImpl的dispatchInvalidateOnAnimation():
1. public void dispatchInvalidateOnAnimation(View view) {
2. mInvalidateOnAnimationRunnable.addView(view);
3. }
mInvalidateOnAnimationRunnable 是一个 InvalidateOnAnimationRunnable:
1. public void addView(View view) {
2. synchronized (this) {
3. mViews.add(view);
4. postIfNeededLocked();
5. }
6. }
而postIfNeededLocked()干的事情就是把mInvalidateOnAnimationRunnable 作为Choreographer.CALLBACK_ANIMATION(这个类型的task会在mesaure/layout/draw之前被运行)的Task 交给 UI线程的Choreographer.