invalidate()函数的主要作用是请求View树进行重绘,该函数可以由应用程序调用,或者由系统函数间接 调用,例如setEnable(), setSelected(), setVisiblity()都会间接调用到invalidate()来请求View树重绘,更新View树的显示。      注:requestLayout()和requestFocus()函数也会引起视图重绘
      下面我们通过源码来了解invalidate()函数的工作原理,首先我们来看View类中invalidate()的实现过程:

  1. /**
  2. * Invalidate the whole view. If the view is visible,
  3. * [url=mailto:{@link]{@link[/url] #onDraw(android.graphics.Canvas)} will be called at some point in
  4. * the future. This must be called from a UI thread. To call from a non-UI thread,
  5. * call [url=mailto:{@link]{@link[/url] #postInvalidate()}.
  6. */
  7. public void invalidate() {
  8. invalidate(true);
  9. }

复制代码

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

  1. /**
  2. * This is where the invalidate() work actually happens. A full invalidate()
  3. * causes the drawing cache to be invalidated, but this function can be called with
  4. * invalidateCache set to false to skip that invalidation step for cases that do not
  5. * need it (for example, a component that remains at the same dimensions with the same
  6. * content).
  7. *
  8. * @param invalidateCache Whether the drawing cache for this view should be invalidated as
  9. * well. This is usually true for a full invalidate, but may be set to false if the
  10. * View's contents or dimensions have not changed.
  11. */
  12. void invalidate(boolean invalidateCache) {
  13. if (ViewDebug.TRACE_HIERARCHY) {
  14. ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
  15. }
  16. if (skipInvalidate()) {
  17. return;
  18. }
  19. if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
  20. (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||
  21. (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {
  22. mLastIsOpaque = isOpaque();
  23. mPrivateFlags &= ~DRAWN;
  24. mPrivateFlags |= DIRTY;
  25. if (invalidateCache) {
  26. mPrivateFlags |= INVALIDATED;
  27. mPrivateFlags &= ~DRAWING_CACHE_VALID;
  28. }
  29. final AttachInfo ai = mAttachInfo;
  30. final ViewParent p = mParent;
  31. //noinspection PointlessBooleanExpression,ConstantConditions
  32. if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
  33. if (p != null && ai != null && ai.mHardwareAccelerated) {
  34. // fast-track for GL-enabled applications; just invalidate the whole hierarchy
  35. // with a null dirty rect, which tells the ViewAncestor to redraw everything
  36. p.invalidateChild(this, null);
  37. return;
  38. }
  39. }
  40. if (p != null && ai != null) {
  41. final Rect r = ai.mTmpInvalRect;
  42. r.set(0, 0, mRight - mLeft, mBottom - mTop);
  43. // Don't call invalidate -- we don't want to internally scroll
  44. // our own bounds
  45. p.invalidateChild(this, r);
  46. }
  47. }
  48. }

复制代码

下面我们来具体进行分析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()的 实现过程:

  1. public final void invalidateChild(View child, final Rect dirty) {
  2. if (ViewDebug.TRACE_HIERARCHY) {
  3. ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
  4. }
  5. ViewParent parent = this;
  6. final AttachInfo attachInfo = mAttachInfo;
  7. if (attachInfo != null) {
  8. // If the child is drawing an animation, we want to copy this flag onto
  9. // ourselves and the parent to make sure the invalidate request goes
  10. // through
  11. final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
  12. if (dirty == null) {
  13. if (child.mLayerType != LAYER_TYPE_NONE) {
  14. mPrivateFlags |= INVALIDATED;
  15. mPrivateFlags &= ~DRAWING_CACHE_VALID;
  16. child.mLocalDirtyRect.setEmpty();
  17. }
  18. do {
  19. View view = null;
  20. if (parent instanceof View) {
  21. view = (View) parent;
  22. if (view.mLayerType != LAYER_TYPE_NONE) {
  23. view.mLocalDirtyRect.setEmpty();
  24. if (view.getParent() instanceof View) {
  25. final View grandParent = (View) view.getParent();
  26. grandParent.mPrivateFlags |= INVALIDATED;
  27. grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
  28. }
  29. }
  30. if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
  31. // already marked dirty - we're done
  32. break;
  33. }
  34. }
  35. if (drawAnimation) {
  36. if (view != null) {
  37. view.mPrivateFlags |= DRAW_ANIMATION;
  38. } else if (parent instanceof ViewRootImpl) {
  39. ((ViewRootImpl) parent).mIsAnimating = true;
  40. }
  41. }
  42. if (parent instanceof ViewRootImpl) {
  43. ((ViewRootImpl) parent).invalidate();
  44. parent = null;
  45. } else if (view != null) {
  46. if ((view.mPrivateFlags & DRAWN) == DRAWN ||
  47. (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
  48. view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
  49. view.mPrivateFlags |= DIRTY;
  50. parent = view.mParent;
  51. } else {
  52. parent = null;
  53. }
  54. }
  55. } while (parent != null);
  56. } else {
  57. // Check whether the child that requests the invalidate is fully opaque
  58. final boolean isOpaque = child.isOpaque() && !drawAnimation &&
  59. child.getAnimation() == null;
  60. // Mark the child as dirty, using the appropriate flag
  61. // Make sure we do not set both flags at the same time
  62. int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
  63. if (child.mLayerType != LAYER_TYPE_NONE) {
  64. mPrivateFlags |= INVALIDATED;
  65. mPrivateFlags &= ~DRAWING_CACHE_VALID;
  66. child.mLocalDirtyRect.union(dirty);
  67. }
  68. final int[] location = attachInfo.mInvalidateChildLocation;
  69. location[CHILD_LEFT_INDEX] = child.mLeft;
  70. location[CHILD_TOP_INDEX] = child.mTop;
  71. Matrix childMatrix = child.getMatrix();
  72. if (!childMatrix.isIdentity()) {
  73. RectF boundingRect = attachInfo.mTmpTransformRect;
  74. boundingRect.set(dirty);
  75. //boundingRect.inset(-0.5f, -0.5f);
  76. childMatrix.mapRect(boundingRect);
  77. dirty.set((int) (boundingRect.left - 0.5f),
  78. (int) (boundingRect.top - 0.5f),
  79. (int) (boundingRect.right + 0.5f),
  80. (int) (boundingRect.bottom + 0.5f));
  81. }
  82. do {
  83. View view = null;
  84. if (parent instanceof View) {
  85. view = (View) parent;
  86. if (view.mLayerType != LAYER_TYPE_NONE &&
  87. view.getParent() instanceof View) {
  88. final View grandParent = (View) view.getParent();
  89. grandParent.mPrivateFlags |= INVALIDATED;
  90. grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
  91. }
  92. }
  93. if (drawAnimation) {
  94. if (view != null) {
  95. view.mPrivateFlags |= DRAW_ANIMATION;
  96. } else if (parent instanceof ViewRootImpl) {
  97. ((ViewRootImpl) parent).mIsAnimating = true;
  98. }
  99. }
  100. // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
  101. // flag coming from the child that initiated the invalidate
  102. if (view != null) {
  103. if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
  104. view.getSolidColor() == 0) {
  105. opaqueFlag = DIRTY;
  106. }
  107. if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
  108. view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
  109. }
  110. }
  111. parent = parent.invalidateChildInParent(location, dirty);
  112. if (view != null) {
  113. // Account for transform on current parent
  114. Matrix m = view.getMatrix();
  115. if (!m.isIdentity()) {
  116. RectF boundingRect = attachInfo.mTmpTransformRect;
  117. boundingRect.set(dirty);
  118. m.mapRect(boundingRect);
  119. dirty.set((int) boundingRect.left, (int) boundingRect.top,
  120. (int) (boundingRect.right + 0.5f),
  121. (int) (boundingRect.bottom + 0.5f));
  122. }
  123. }
  124. } while (parent != null);
  125. }
  126. }
  127. }

复制代码

大概流程如下,我们主要关注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变量中

  1. /**
  2. * Don't call or override this method. It is used for the implementation of
  3. * the view hierarchy.
  4. *
  5. * This implementation returns null if this ViewGroup does not have a parent,
  6. * if this ViewGroup is already fully invalidated or if the dirty rectangle
  7. * does not intersect with this ViewGroup's bounds.
  8. */
  9. public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
  10. if (ViewDebug.TRACE_HIERARCHY) {
  11. ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
  12. }
  13. if ((mPrivateFlags & DRAWN) == DRAWN ||
  14. (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
  15. if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
  16. FLAG_OPTIMIZE_INVALIDATE) {
  17. dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
  18. location[CHILD_TOP_INDEX] - mScrollY);
  19. final int left = mLeft;
  20. final int top = mTop;
  21. if ((mGroupFlags & FLAG_CLIP_CHILDREN) != FLAG_CLIP_CHILDREN ||
  22. dirty.intersect(0, 0, mRight - left, mBottom - top) ||
  23. (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
  24. mPrivateFlags &= ~DRAWING_CACHE_VALID;
  25. location[CHILD_LEFT_INDEX] = left;
  26. location[CHILD_TOP_INDEX] = top;
  27. if (mLayerType != LAYER_TYPE_NONE) {
  28. mLocalDirtyRect.union(dirty);
  29. }
  30. return mParent;
  31. }
  32. } else {
  33. mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
  34. location[CHILD_LEFT_INDEX] = mLeft;
  35. location[CHILD_TOP_INDEX] = mTop;
  36. if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
  37. dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
  38. } else {
  39. // in case the dirty rect extends outside the bounds of this container
  40. dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
  41. }
  42. if (mLayerType != LAYER_TYPE_NONE) {
  43. mLocalDirtyRect.union(dirty);
  44. }
  45. return mParent;
  46. }
  47. }
  48. return null;
  49. }

复制代码


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

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

  1. public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
  2. invalidateChild(null, dirty);
  3. return null;
  4. }

复制代码

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

  1. public void invalidateChild(View child, Rect dirty) {
  2. checkThread();
  3. if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
  4. if (dirty == null) {
  5. // Fast invalidation for GL-enabled applications; GL must redraw everything
  6. invalidate();
  7. return;
  8. }
  9. if (mCurScrollY != 0 || mTranslator != null) {
  10. mTempRect.set(dirty);
  11. dirty = mTempRect;
  12. if (mCurScrollY != 0) {
  13. dirty.offset(0, -mCurScrollY);
  14. }
  15. if (mTranslator != null) {
  16. mTranslator.translateRectInAppWindowToScreen(dirty);
  17. }
  18. if (mAttachInfo.mScalingRequired) {
  19. dirty.inset(-1, -1);
  20. }
  21. }
  22. if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
  23. mAttachInfo.mSetIgnoreDirtyState = true;
  24. mAttachInfo.mIgnoreDirtyState = true;
  25. }
  26. mDirty.union(dirty);
  27. if (!mWillDrawSoon) {
  28. scheduleTraversals();
  29. }
  30. }

复制代码

具体分析如下:            1、判断此次调用是否在UI线程中进行

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

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

至此一次invalidate()就结束了

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

android invalidate 执行流程详解的更多相关文章

  1. springmvc的执行流程详解

    1.什么是MVC MVC是Model View Controller的缩写,它是一个设计模式 2.springmvc执行流程详细介绍 第一步:发起请求到前端控制器(DispatcherServlet) ...

  2. Spring 框架基础(06):Mvc架构模式简介,执行流程详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.SpringMvc框架简介 1.Mvc设计理念 MVC是一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集 ...

  3. redux基础概念及执行流程详解

    一.执行流程 全局有一个公共的容器(所有组件都可以操作),我们可以在某个组件中把全局容器中的信息进行修改,而只要全局信息修改,就可以通知所有用到该信息的组件重新渲染(类似于发布订阅)==>red ...

  4. Spark SQL底层执行流程详解

    本文目录 一.Apache Spark 二.Spark SQL发展历程 三.Spark SQL底层执行原理 四.Catalyst 的两大优化 一.Apache Spark Apache Spark是用 ...

  5. AngularJS执行流程详解

    一.启动阶段 大家应该都知道,当浏览器加载一个HTML页面时,它会将HMTL页面先解析成DOM树,然后逐个加载DOM树中的每一个元素节点.我们可以把AngularJS当做一个类似jQuery的js库, ...

  6. AngularJS执行流程详解(转)

    一.启动阶段 大家应该都知道,当浏览器加载一个HTML页面时,它会将HMTL页面先解析成DOM树,然后逐个加载DOM树中的每一个元素节点.我们可以把AngularJS当做一个类似jQuery的js库, ...

  7. unity3d-配置Android环境,打包发布Apk流程详解

    31:unity3d-配置Android环境,打包发布Apk流程详解 作者 阿西纳尼 关注 2016.08.28 22:52 字数 498 阅读 1806评论 0喜欢 5 Unity配置Android ...

  8. 给 Android 开发者的 RxJava 详解

    我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个 ...

  9. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

随机推荐

  1. 第 2 章 代理模式【Proxy Pattern】

    第 2 章 代理模式[Proxy Pattern] 以下内容出自:24种设计模式介绍与6大设计原则.pdf 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道被代 ...

  2. JavaScript自学代码--(三)

    //通过 id 查找 HTML 元素 var x = document.getElementById("demo"); //通过标签名查找 HTML 元素 //本例查找 id=&q ...

  3. Android使用pull解析xml

    一.理论准备     Pull解析器的运行方式与 SAX 解析器相似.它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件.跟SAX不同的是, ...

  4. backbone csdn

    http://blog.csdn.net/the_fire/article/details/7444067 blog.csdn.net/the_fire/article/details/7445448 ...

  5. IntelliJ IDEA SVN的账号修改 信息清除

    来到编译器的setting设置 搜索subversion 点击subversion 找到下面的clear auth...按钮,点击一下 就可以了

  6. C++内存管理(超长,例子很详细,排版很好)

    [导语] 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不 ...

  7. Android JNI使用方法

    经过几天的努力终于搞定了android JNI部分,下面将我的这个小程序和大家分享一下.android JNI是连接android Java部分和C/C++部分的纽带,完整使用JNI需要Java代码和 ...

  8. 转自 x_x_的百度空间 搞ACM的你伤不起

    来源:http://roba.rushcj.com/?p=548 劳资六年前开始搞ACM啊!!!!!!!!!! 从此踏上了尼玛不归路啊!!!!!!!!!!!! 谁特么跟劳资讲算法是程序设计的核心啊!! ...

  9. 剧烈变化的移动互联网O2O

  10. Windows Server AppFabric 安装文档

    安装指南 入门标题页 3 Windows Server AppFabric 安装和配置指南 3 版权 3 版权所有 3 简介 3 清单:规划安装 4 硬件要求 4 使计算机作好安装准备 5 本节内容 ...