当前位置: 代码迷 >> Android >> Android中invalidate() 函数详解(组合Android 4.0.4 最新源码)
  详细解决方案

Android中invalidate() 函数详解(组合Android 4.0.4 最新源码)

热度:12   发布时间:2016-05-01 18:08:17.0
Android中invalidate() 函数详解(结合Android 4.0.4 最新源码)

      invalidate()函数的主要作用是请求View树进行重绘,该函数可以由应用程序调用,或者由系统函数间接调用,例如setEnable(), setSelected(), setVisiblity()都会间接调用到invalidate()来请求View树重绘,更新View树的显示。

      注:requestLayout()和requestFocus()函数也会引起视图重绘

      下面我们通过源码来了解invalidate()函数的工作原理,首先我们来看View类中invalidate()的实现过程:


      invalidate()函数会转而调用invalidate(true),继续往下看:


      下面我们来具体进行分析invalidate(true)函数的执行流程:

            1、首先调用skipInvalidate(),该函数主要判断该View是否不需要重绘,如果不许要重绘则直接返回,不需要重绘的条件是该View不可见并且未进行动画

            2、接下来的if语句是来进一步判断View是否需要绘制,其中表达式 (mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)的意思指的是如果View需要重绘并且其大小不为0,其余几个本人也未完全理解,还望高手指点~~如果需要重绘,则处理相关标志位

            3、对于开启硬件加速的应用程序,则调用父视图的invalidateChild函数绘制整个区域,否则只绘制dirty区域(r变量所指的区域),这是一个向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新Rect做交集。

      接下来看invalidateChild()的 实现过程:


      大概流程如下,我们主要关注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区域进行计算

      以上过程的主体是一个do{}while{}循环,不断的将子视图的dirty区域与父视图做运算来确定最终要重绘的dirty区域,最终循环到ViewRoot(ViewRoot的parent为null)为止,并将dirty区域保存到ViewRoot的mDirty变量中


      该函数首先调用offset将子视图的坐标位置转换为在父视图(当前视图)的显示位置,这里主要考虑scroll后导致子视图在父视图中的显示区域会发生变化,接着调用union函数求得当前视图与子视图的交集,求得的交集必定是小于dirty的范围,因为子视图的dirty区域有可能超出其父视图(当前视图)的范围,最后返回当前视图的父视图。

      再来看ViewRoot中invalidateChildInparent的执行过程:


      该函数仅仅调用了ViewRoot的invalidateChild,下面继续看invalidateChild的源码:

      具体分析如下:

            1、判断此次调用是否在UI线程中进行

            2、将dirty的坐标位置转换为ViewRoot的屏幕显示区域

            3、更新mDirty变量,并调用scheduleTraversals发起重绘请求

      至此一次invalidate()就结束了

      总结:invalidate主要给需要重绘的视图添加DIRTY标记,并通过和父视图的矩形运算求得真正需要绘制的区域,并保存在ViewRoot中的mDirty变量中,最后调用scheduleTraversals发起重绘请求,scheduleTraversals会发送一个异步消息,最终调用performTraversals()执行重绘,performTraversals()的具体过程以后再分析。


    以上所有代码基于Android 4.0.4,并结合《Android内核剖析》分析总结而成,源码中涉及到的部分细节本人也未完全理解,还望高手指点~~

  相关解决方案