Android invalidate()源码分析

来源:互联网 发布:linux保存命令 编辑:程序博客网 时间:2024/05/16 15:53

Android View源码中的invalidate()在开发中经常使用,尤其是自定义控件,还有View的动画基本都是直接调用该方法引起重绘。该方法会使View全部或者部分重绘,具体取决于传入参数、View透明度、View是否在动画以及View是否开启硬件加速绘制等。其主要调用流程如下:

invalidate()分析
简单总结如下:

1.直接调用invalidate()函数,其内部实际是调用invalidate(true)。这里的参数代表是否是完全重绘。完全重绘与否其实就是是否使用绘制缓存去绘制的问题。

2.在invalidate(true)函数中:

(1)首先去确认该View是否需要跳过绘制,这里其实就是一个判断条件。跳过绘制条件:View不是可见的 && 存在动画对象 && 父视图不是ViewGroup或者不是过渡态。只有满足以上条件才可以跳过绘制直接返回。

(2)接着要确认在如下条件才可以重绘:正在动画或者View大小不是0 || 需要完整绘制绘制并且绘制缓存可用 || 未重绘过 || 透明度和上次比较有了变化。满足以上条件中的一个就可以执行重绘。

(3)对是否完全重绘设置,即该函数传入的参数。其实就是设置绘制缓存不可用的标识到mPrivateFlags。在绘制的时候会根据该标识决定是否使用绘制缓存。当然如果是绘制缓存可用的时候需要在绘制的时候先绘制View到Bitmap缓存,如果已经存在该缓存就直接将缓存绘制到canvas。如果是完整重绘就跳过绘制缓存和使用绘制缓存的步骤直接去重绘View到canvas。

(4)接下来就要去ViewParent中去执行。在执行之前分了两种情况去处理,分别是硬件加速可用和不可用的情况。在硬件加速可用的情况传入invalidateChild()的dirty参数为null,代表需要重绘这个View层级。而使用软件绘制时传入的是根据View大小构造的dirty矩阵。需要指出这里的ViewParent一般是值ViewGroup。这里只分析软件绘制。

3.在ViewGroup中去执行invalidateChild():

(1)初始化变量:是否在动画中、View变化矩阵、是否不透明、记录子View相对于父ViewGroup的相对坐标。不透明判断主要是根据:需要重绘child的不透明度 && 无动画 && child变化矩阵无变化。记录坐标主要是为了将带绘制的dirty矩阵与ViewGroup可显示矩阵做相交或者相并处理。

(2)处理child变化矩形与ViewGroup变换对象对dirty矩阵的影响。首先对ViewGroup设置的静态变换对象做处理,获取设置的变换对象然后获取其变换矩阵,将child的原矩阵和获取的矩阵做合并处理,最后将处理之后的矩阵应用到dirty矩阵,同时也将child的变化矩阵应用到dirty矩阵。

(3)do-while循环设置ViewGroup属性同时处理该child的dirty矩阵与ViewGroup可显示矩阵的关系:

  • 如果正在动画设置分ViewParent类型设置动画标识。

  • 设置ViewGroup的DIRTY标识待重绘。

  • 调用invalidateChildInParent()处理该child的dirty矩阵与ViewGroup可显示矩阵的关系,同时返回该ViewGroup的ViewParent以便下次循环接着调用。

  • 经过上面步骤的dirty矩阵此时已经与ViewGroup可显示矩阵做处理了。如果是ViewGroup将其变化的变换矩阵应用与child的dirty矩阵。

4.在以上循环中调用的invalidateChildInParent()由两种实现。分别是在ViewGroup中实现和ViewRootImpl实现。该循环结束前的最后一次调用一定是ViewRootImpl的实现。

(1)对ViewGroup中的实现做介绍。

  • 首先判断条件:存在动画 || 动画缓存可用。只有该条件满足才会执行否则直接返回null。

  • 接着判断条件:如果是已完成动画或者是没有动画时。首先将dirty位置坐标偏移至相对于父View可视区域原点的坐标。接着处理如果不需要剪裁child直接将dirty与ViewGroup整体大小求并集。之所以需要ViewGroup整体大小求并集是因为涉及到子View动画,ViewGroup需要重绘自身完整区域。如果需要剪裁child则将dirty矩阵与ViewGroup整体大小求交集,如果不存在交集则直接置空dirty矩阵。接着更新location为该ViewGroup相对于其ViewParent的相对坐标。最后返回其ViewParent。

  • 如果是以上判断的对立面,即正在动画中:更新location为该ViewGroup相对于其ViewParent的相对坐标。如果需要剪裁child则直接设置dirty为该ViewGroup整体的大小。如果不需要直接dirty与整体大小求并集。最后返回其ViewParent。

可以看到以上两点的处理是VIew是否动画为主。如果在动画最后dirty处理完要么是ViewGroup整体大小要么比他大。如果不在动画则有可能小于ViewGroup整体大小。这也是子View动画会完全影响到其ViewGroup绘制的原因。

(2)对ViewRootImpl的实现介绍。

  • 首先检测该线程是否是UI线程,如果不是直接抛出异常。

  • 对dirty检查处理。如果是null,直接设置dirty为整个大小重绘。如果是空并且不在动画直接返回null。如果ViewRoot存在偏移mCurScrollY转化dirty位置到相对于ViewRoot可视区域的坐标。

  • 将dirty添加到待重绘区域。获取上次重绘的矩阵localDirty与dirty求并集。处理窗口缩放然后将处理后的大小与localDirty求交集,如果没有交集置空localDirty。

  • 重绘任务。满足如下条件:performTraversal()未执行 && localDirty与窗口存在交集或者正在动画。调用任务scheduleTraversals();最后返回null。

所以最后一次do循环一定是调用ViewRootImpl中的该方法实现,最后执行绘制任务的。

至此,invalidate()源码介绍完毕。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 摔倒了膝盖摔肿了又痛怎么办 厕所堵了怎么办疏通马桶有妙招 月经不来怎么办如何让它快点来 苹果手机进水了开不了机怎么办 苹果5s进水了怎么办修要多少钱 吃了过期3年的药怎么办 离婚后孩子的抚养费不给怎么办 小车钥匙丢了怎么办配要多少钱 一键启动的车钥匙丢了怎么办 股票退市了手里的股票怎么办 3d硬金以后要换怎么办 偏指甲红肿长在了肉里怎么办 牙齿黄怎么办教你牙齿美白小窍门 2岁孩门牙磕断了怎么办 脸上用了含激素的产品应该怎么办 身上起红疙瘩水泡很痒怎么办 怀疑老公有外遇他不承认怎么办 苹果手机锁屏密码忘了怎么办 黑色t恤洗了掉毛怎么办 老婆要和我离婚我该怎么办 发现老婆有外遇最明智的怎么办 高度近视怎么办我快一千度近 天刀耐久度为0了怎么办 新车被4s店装了怎么办 h面和w面联系线怎么办 造梦西游3到80级怎么办 加95的车加了92怎么办 95的车加92的油怎么办 宝宝六个月了奶水变少了怎么办 六个月的宝宝不爱吃奶粉怎么办 脸上挤黑头留下的小坑怎么办 小孩身上起红疙瘩很痒怎么办 肛门上长了个肉疙瘩怎么办 脚撞了一下肿了怎么办u 怀孕9个月同床了怎么办 结婚两年了都没怀孕怎么办 怀孕快40周了没有生的迹象怎么办 生完小孩掉头发很厉害怎么办 我27岁欠50多万怎么办 8岁童牙齿摔松了怎么办 我的眉毛后半边很稀少怎么办