android invalidate 执行流程详解
invalidate()函数的主要作用是请求View树进行重绘,该函数可以由应用程序调用,或者由系统函数间接 调用,例如setEnable(), setSelected(), setVisiblity()都会间接调用到invalidate()来请求View树重绘,更新View树的显示。 注:requestLayout()和requestFocus()函数也会引起视图重绘
下面我们通过源码来了解invalidate()函数的工作原理,首先我们来看View类中invalidate()的实现过程:
- /**
- * Invalidate the whole view. If the view is visible,
- * [url=mailto:{@link]{@link[/url] #onDraw(android.graphics.Canvas)} will be called at some point in
- * the future. This must be called from a UI thread. To call from a non-UI thread,
- * call [url=mailto:{@link]{@link[/url] #postInvalidate()}.
- */
- public void invalidate() {
- invalidate(true);
- }
复制代码
invalidate()函数会转而调用invalidate(true),继续往下看:
- /**
- * This is where the invalidate() work actually happens. A full invalidate()
- * causes the drawing cache to be invalidated, but this function can be called with
- * invalidateCache set to false to skip that invalidation step for cases that do not
- * need it (for example, a component that remains at the same dimensions with the same
- * content).
- *
- * @param invalidateCache Whether the drawing cache for this view should be invalidated as
- * well. This is usually true for a full invalidate, but may be set to false if the
- * View's contents or dimensions have not changed.
- */
- void invalidate(boolean invalidateCache) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
- }
- if (skipInvalidate()) {
- return;
- }
- if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
- (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||
- (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {
- mLastIsOpaque = isOpaque();
- mPrivateFlags &= ~DRAWN;
- mPrivateFlags |= DIRTY;
- if (invalidateCache) {
- mPrivateFlags |= INVALIDATED;
- mPrivateFlags &= ~DRAWING_CACHE_VALID;
- }
- final AttachInfo ai = mAttachInfo;
- final ViewParent p = mParent;
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
- if (p != null && ai != null && ai.mHardwareAccelerated) {
- // fast-track for GL-enabled applications; just invalidate the whole hierarchy
- // with a null dirty rect, which tells the ViewAncestor to redraw everything
- p.invalidateChild(this, null);
- return;
- }
- }
- if (p != null && ai != null) {
- final Rect r = ai.mTmpInvalRect;
- r.set(0, 0, mRight - mLeft, mBottom - mTop);
- // Don't call invalidate -- we don't want to internally scroll
- // our own bounds
- p.invalidateChild(this, r);
- }
- }
- }
复制代码
下面我们来具体进行分析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()的 实现过程:
- public final void invalidateChild(View child, final Rect dirty) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
- }
- ViewParent parent = this;
- final AttachInfo attachInfo = mAttachInfo;
- if (attachInfo != null) {
- // If the child is drawing an animation, we want to copy this flag onto
- // ourselves and the parent to make sure the invalidate request goes
- // through
- final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
- if (dirty == null) {
- if (child.mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= INVALIDATED;
- mPrivateFlags &= ~DRAWING_CACHE_VALID;
- child.mLocalDirtyRect.setEmpty();
- }
- do {
- View view = null;
- if (parent instanceof View) {
- view = (View) parent;
- if (view.mLayerType != LAYER_TYPE_NONE) {
- view.mLocalDirtyRect.setEmpty();
- if (view.getParent() instanceof View) {
- final View grandParent = (View) view.getParent();
- grandParent.mPrivateFlags |= INVALIDATED;
- grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
- }
- }
- if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
- // already marked dirty - we're done
- break;
- }
- }
- if (drawAnimation) {
- if (view != null) {
- view.mPrivateFlags |= DRAW_ANIMATION;
- } else if (parent instanceof ViewRootImpl) {
- ((ViewRootImpl) parent).mIsAnimating = true;
- }
- }
- if (parent instanceof ViewRootImpl) {
- ((ViewRootImpl) parent).invalidate();
- parent = null;
- } else if (view != null) {
- if ((view.mPrivateFlags & DRAWN) == DRAWN ||
- (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
- view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
- view.mPrivateFlags |= DIRTY;
- parent = view.mParent;
- } else {
- parent = null;
- }
- }
- } while (parent != null);
- } else {
- // Check whether the child that requests the invalidate is fully opaque
- final boolean isOpaque = child.isOpaque() && !drawAnimation &&
- child.getAnimation() == null;
- // Mark the child as dirty, using the appropriate flag
- // Make sure we do not set both flags at the same time
- int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
- if (child.mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= INVALIDATED;
- mPrivateFlags &= ~DRAWING_CACHE_VALID;
- child.mLocalDirtyRect.union(dirty);
- }
- final int[] location = attachInfo.mInvalidateChildLocation;
- location[CHILD_LEFT_INDEX] = child.mLeft;
- location[CHILD_TOP_INDEX] = child.mTop;
- Matrix childMatrix = child.getMatrix();
- if (!childMatrix.isIdentity()) {
- RectF boundingRect = attachInfo.mTmpTransformRect;
- boundingRect.set(dirty);
- //boundingRect.inset(-0.5f, -0.5f);
- childMatrix.mapRect(boundingRect);
- dirty.set((int) (boundingRect.left - 0.5f),
- (int) (boundingRect.top - 0.5f),
- (int) (boundingRect.right + 0.5f),
- (int) (boundingRect.bottom + 0.5f));
- }
- do {
- View view = null;
- if (parent instanceof View) {
- view = (View) parent;
- if (view.mLayerType != LAYER_TYPE_NONE &&
- view.getParent() instanceof View) {
- final View grandParent = (View) view.getParent();
- grandParent.mPrivateFlags |= INVALIDATED;
- grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;
- }
- }
- if (drawAnimation) {
- if (view != null) {
- view.mPrivateFlags |= DRAW_ANIMATION;
- } else if (parent instanceof ViewRootImpl) {
- ((ViewRootImpl) parent).mIsAnimating = true;
- }
- }
- // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
- // flag coming from the child that initiated the invalidate
- if (view != null) {
- if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
- view.getSolidColor() == 0) {
- opaqueFlag = DIRTY;
- }
- if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
- view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
- }
- }
- parent = parent.invalidateChildInParent(location, dirty);
- if (view != null) {
- // Account for transform on current parent
- Matrix m = view.getMatrix();
- if (!m.isIdentity()) {
- RectF boundingRect = attachInfo.mTmpTransformRect;
- boundingRect.set(dirty);
- m.mapRect(boundingRect);
- dirty.set((int) boundingRect.left, (int) boundingRect.top,
- (int) (boundingRect.right + 0.5f),
- (int) (boundingRect.bottom + 0.5f));
- }
- }
- } while (parent != null);
- }
- }
- }
复制代码
大概流程如下,我们主要关注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变量中
- /**
- * Don't call or override this method. It is used for the implementation of
- * the view hierarchy.
- *
- * This implementation returns null if this ViewGroup does not have a parent,
- * if this ViewGroup is already fully invalidated or if the dirty rectangle
- * does not intersect with this ViewGroup's bounds.
- */
- public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
- }
- if ((mPrivateFlags & DRAWN) == DRAWN ||
- (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
- if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
- FLAG_OPTIMIZE_INVALIDATE) {
- dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
- location[CHILD_TOP_INDEX] - mScrollY);
- final int left = mLeft;
- final int top = mTop;
- if ((mGroupFlags & FLAG_CLIP_CHILDREN) != FLAG_CLIP_CHILDREN ||
- dirty.intersect(0, 0, mRight - left, mBottom - top) ||
- (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
- mPrivateFlags &= ~DRAWING_CACHE_VALID;
- location[CHILD_LEFT_INDEX] = left;
- location[CHILD_TOP_INDEX] = top;
- if (mLayerType != LAYER_TYPE_NONE) {
- mLocalDirtyRect.union(dirty);
- }
- return mParent;
- }
- } else {
- mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
- location[CHILD_LEFT_INDEX] = mLeft;
- location[CHILD_TOP_INDEX] = mTop;
- if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
- dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
- } else {
- // in case the dirty rect extends outside the bounds of this container
- dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
- }
- if (mLayerType != LAYER_TYPE_NONE) {
- mLocalDirtyRect.union(dirty);
- }
- return mParent;
- }
- }
- return null;
- }
复制代码
该
函数首先调用offset将子视图的坐标位置转换为在父视图(当前视图)的显示位置,这里主要考虑scroll后导致子视图在父视图中的显示区域会发生变
化,接着调用union函数求得当前视图与子视图的交集,求得的交集必定是小于dirty的范围,因为子视图的dirty区域有可能超出其父视图(当前视
图)的范围,最后返回当前视图的父视图。
再来看ViewRoot中invalidateChildInparent的执行过程:
- public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
- invalidateChild(null, dirty);
- return null;
- }
复制代码
该函数仅仅调用了ViewRoot的invalidateChild,下面继续看invalidateChild的源码:
- public void invalidateChild(View child, Rect dirty) {
- checkThread();
- if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
- if (dirty == null) {
- // Fast invalidation for GL-enabled applications; GL must redraw everything
- invalidate();
- return;
- }
- if (mCurScrollY != 0 || mTranslator != null) {
- mTempRect.set(dirty);
- dirty = mTempRect;
- if (mCurScrollY != 0) {
- dirty.offset(0, -mCurScrollY);
- }
- if (mTranslator != null) {
- mTranslator.translateRectInAppWindowToScreen(dirty);
- }
- if (mAttachInfo.mScalingRequired) {
- dirty.inset(-1, -1);
- }
- }
- if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
- mAttachInfo.mSetIgnoreDirtyState = true;
- mAttachInfo.mIgnoreDirtyState = true;
- }
- mDirty.union(dirty);
- if (!mWillDrawSoon) {
- scheduleTraversals();
- }
- }
复制代码
具体分析如下: 1、判断此次调用是否在UI线程中进行
2、将dirty的坐标位置转换为ViewRoot的屏幕显示区域
3、更新mDirty变量,并调用scheduleTraversals发起重绘请求
至此一次invalidate()就结束了
总结:invalidate主要给需要重绘的视图添加DIRTY标记,并通过和父视图的矩形运算求得真正需要绘制的区域,并保存在ViewRoot中的
mDirty变量中,最后调用scheduleTraversals发起重绘请求,scheduleTraversals会发送一个异步消息,最终调用
performTraversals()执行重绘,performTraversals()的具体过程以后再分析。
android invalidate 执行流程详解的更多相关文章
- springmvc的执行流程详解
1.什么是MVC MVC是Model View Controller的缩写,它是一个设计模式 2.springmvc执行流程详细介绍 第一步:发起请求到前端控制器(DispatcherServlet) ...
- Spring 框架基础(06):Mvc架构模式简介,执行流程详解
本文源码:GitHub·点这里 || GitEE·点这里 一.SpringMvc框架简介 1.Mvc设计理念 MVC是一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集 ...
- redux基础概念及执行流程详解
一.执行流程 全局有一个公共的容器(所有组件都可以操作),我们可以在某个组件中把全局容器中的信息进行修改,而只要全局信息修改,就可以通知所有用到该信息的组件重新渲染(类似于发布订阅)==>red ...
- Spark SQL底层执行流程详解
本文目录 一.Apache Spark 二.Spark SQL发展历程 三.Spark SQL底层执行原理 四.Catalyst 的两大优化 一.Apache Spark Apache Spark是用 ...
- AngularJS执行流程详解
一.启动阶段 大家应该都知道,当浏览器加载一个HTML页面时,它会将HMTL页面先解析成DOM树,然后逐个加载DOM树中的每一个元素节点.我们可以把AngularJS当做一个类似jQuery的js库, ...
- AngularJS执行流程详解(转)
一.启动阶段 大家应该都知道,当浏览器加载一个HTML页面时,它会将HMTL页面先解析成DOM树,然后逐个加载DOM树中的每一个元素节点.我们可以把AngularJS当做一个类似jQuery的js库, ...
- unity3d-配置Android环境,打包发布Apk流程详解
31:unity3d-配置Android环境,打包发布Apk流程详解 作者 阿西纳尼 关注 2016.08.28 22:52 字数 498 阅读 1806评论 0喜欢 5 Unity配置Android ...
- 给 Android 开发者的 RxJava 详解
我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个 ...
- Android事件传递机制详解及最新源码分析——ViewGroup篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...
随机推荐
- org.springframework.web.bind.ServletRequestDataBinde
org.springframework.validation Class DataBinder java.lang.Object org.springframework.validation.Data ...
- BlockingQueue-线程的阻塞队列
BlockingQueue作为线程容器,可以为线程同步提供有力的保障,其主要用到的方法包括: add(E o); //将指定的元素添加到此队列中(如果立即可行),在成功时返回 true,其他情况则抛出 ...
- POJ_2392_Space_Elevator_(动态规划,背包)
描述 http://poj.org/problem?id=2392 磊方块,每种方块有数量,高度,以及该种方块所能处在的最高高度.问最高磊多高? Space Elevator Time Limit: ...
- WordPress RokMicroNews插件‘thumb.php’ 多个安全漏洞
漏洞名称: WordPress RokMicroNews插件‘thumb.php’ 多个安全漏洞 CNNVD编号: CNNVD-201309-384 发布时间: 2013-09-24 更新时间: 20 ...
- 三招搞挂Mysql(转)
一.产生大量的undo日志 众所周知,InnoDB是一个支持MVCC的存储引擎,为了支持MVCC,InnoDB需要保存undo日志,以便对用户提供记录的历史版本.如果我们开启一个事务,反复地更新一条记 ...
- Unity3d Fast Indirect illumination Using Two Virtual Spherical Gaussian Lights-Square Enix论文
博主实现(in Unity3d 5) used one spotlight 史克威尔效果展示(夜光引擎?) 博主近期渲染:最近用unity5弄的一些渲染 ---- by wolf96 http:// ...
- JS实现表单输入Enter键转换焦点框
<form> <input type="text" onkeypress="return handleEnter(this, event)"& ...
- Ubuntu 下安装Kibana和logstash
原文地址:http://www.cnblogs.com/saintaxl/p/3946667.html 简单来讲他具体的工作流程就是 logstash agent 监控并过滤日志,将过滤后的日志内容发 ...
- 让你的Git水平更上一层楼的10个小贴士
注意:本文中,一些命令包含含有方括号的部分(e.g.git add -p [file_name]).在这些例子中,您要在该处插入所需的数字,标示符等.而不需要保留方括号. 1.Git自动补全 如果你在 ...
- linux 多线程基础3
一.线程属性 线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化.我们用pthread_attr_init函数对其初始化,用pthrea ...