invalidate()、postInvalidate()、postInvalidateOnAnimation() 三者的概念,区别及使用场景

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(thisnull);  

27.                     return;  

28.                 }  

29.             }  

30.   

31.             if (p != null && ai != null) {  

32.                 final Rect r = ai.mTmpInvalRect;  

33.                 r.set(00, 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. }  

ViewRootImpldispatchInvalidateOnAnimation():

 

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.

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值