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. 我的pch文件

    /** *  1. RGB背景色 */ #define PPCOLOR_RGB(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue: ...

  2. ASP.NET之HttpModule拦截404异常

    Httpmodule代码: public class Error404Module : IHttpModule { public void Init(HttpApplication context) ...

  3. Spring In Action 第4版笔记-第一章-001架构

    1.Spring’s fundamental mission: Spring simplifies Java development. 2.To back up its attack on Java ...

  4. Mozilla研究—深入理解mozilla所需的背景知识

    mozilla是一个以浏览器为中心的软件平台,它在我们平台中占有重要地位.我们用它来实现WEB浏览器.WAP浏览器.邮件系统.电子书和帮助阅读器等应用程序.为此,我最近花了不少时间去阅读mozilla ...

  5. Deep Learning论文笔记之(八)Deep Learning最新综述

    Deep Learning论文笔记之(八)Deep Learning最新综述 zouxy09@qq.com http://blog.csdn.net/zouxy09 自己平时看了一些论文,但老感觉看完 ...

  6. 17.1.1.3 Creating a User for Replication 创建一个用于用于复制:

    17.1.1.3 Creating a User for Replication 创建一个用于用于复制: 每个slave 连接到master 使用一个MySQL 用户名和密码, 因此必须有一个用户账户 ...

  7. 17.1 Replication Configuration 复制配置

    17.1 Replication Configuration 复制配置 17.1.1 How to Set Up Replication 17.1.2 Replication Formats 17.1 ...

  8. 【HDOJ】2732 Leapin' Lizards

    贪心+网络流.对于每个结点,构建入点和出点.对于每一个lizard>0,构建边s->in position of lizard, 容量为1.对于pillar>0, 构建边in pos ...

  9. Learning WCF Chapter2 Data Contracts

    A data contract describes how CLR types map to XSD schema definitions. Data contracts are the prefer ...

  10. java 正则表达式例子, 查找字符串

    import java.util.regex.Matcher;import java.util.regex.Pattern; public class Main { public static voi ...