《Android内核剖析》读书笔记 第13章 View工作原理【View树遍历】
View状态分类
在View视图中定义了多种和界面效果相关的状态,比如拥有焦点Focused、按下Pressed等,不同的状态一般会显示不同的界面效果,而且视图状态会随着用户的操作而改变,一般通过xml文件中selector来申明各种状态下使用的背景图;所有的状态码位于StateListDrawable中,常用的状态码包括:
- enable:当前View是否可用,开发者可以通过setEnable()改变,他完全由开发者控制;
当状态为不可用时,View将不会响应任何事件; - focused:当前View是否正拥有焦点,一个窗口中只能有一个View拥有焦点,一般随用户操作而动态改变;
该状态主要是针对按键的,因为所有的按键消息都将派发给focused视图; - pressed:当前View是否正被按下,主要是针对触摸消息的,一般当用户按下时视图会有一个明显的变化,也是随用户操作而动态改变;
- selected:当前View是否已被选中,一个窗口中可以有多个视图处于选中状态;开发者可以通过setSelected()改变,他完全由开发者控制;
导致View树重新遍历的总体诱因
遍历View树意味着整个View需要重新对其包含的子视图分配大小并重绘;一般情况下导致重新遍历的原因有三个:其一,视图本身内部状态发生变化,比如显示属性由GONE到VISIBLE;其二,ViewGroup中添加或删除了视图导致需要重新为子视图分配位置;其三,视图本身的大小发生变化,比如TextView中的文本内容变多变少了;
在代码层面这三种情况最后都会直接或间接调用到View中的三个函数:requestLayout/requestFocus/invalidate;由于是View树遍历,所以最后都会执行到最顶级父视图中的ViewRootImpl.scheduleTraversals();在该方法内,系统会发起一个异步消息(老版本中直接通过Handler发,新版本4.1中引入了Choreographer,以及对VSync和三级Buffer支持,让页面显示和操作更流畅,具体可以详见《Android Project Butter分析》),然后在异步消息执行过程中调用performTraversals()完成具体的View树遍历;可以参见下图:
View中超多的属性变量如何管理?
在庞大的View类中会涉及到非常多的状态码,比如是否可用、是否处于按下状态、是否需要重新分配位置、是否需要重绘等等;View树在遍历重绘时会根据不同的变量值来进行相应的操作,为此View中引入了bit标示位来管理这些状态值,分别用mViewFlags和mPrivateFlags变量来管理(随着状态码的增加,在新版本4.2中还有mPrivateFlags2/mPrivateFlags3变量),他们都是int类型的,也就是说理论上每个变量可以用来标示32个状态值,当对各个状态值修改时采用位运算符&|来完成;
其中mViewFlags变量主要用来保存和视图状态相关的值,比如是否可单击、是否可双击、是否可用、是否拥有焦点等;
mPrivateFlags变量主要用来保存和内部逻辑相关的属性,比如是否需要重新分配位置、是否需要重绘、是否刷新View缓存等;
注意:这两个变量之间是有紧密联系的,经常会需要两个变量同时设置某些状态值,可以参见setFlags(..)方法中的具体内容;
requestLayout()
该方法的执行过程很简单,因为当View树进行重新布局时,总是重新给所有的视图都进行布局,而不像重绘是可以指定只绘制某一个小区域的;
从代码层面他只是为mPrivateFlags变量添加FORCE_LAYOUT标识而已;然后逐层请求mParent.requestLayout();详见下图:
invalidate()
该方法的作用是请求View树重绘;视图及其父视图在界面上是分层先后显示的,父视图位于子视图下面,绘制过程中,首先绘制最底层的根视图,然后绘制其包含的子视图,子视图若是ViewGroup,则继续绘制其子视图,如此迭代至没有子视图为止;
在具体的重绘过程中,一般不会对所有视图都进行重绘,而是只绘制那些“需要绘制”的视图,那如何找出“需要绘制”的区域呢?这就是invalidate方法要完成的功能!
大致的思路是:当View需要重绘时会给mPrivateFlags变量添加DRAWN标识,然后根据所有带该标识的视图边界一起确定最终要重绘的矩形区块,这里面会涉及到不同坐标体系间的换算,可以参见下图:
代码的具体执行过程是:
- View.invalidate()中设置必要的状态位标识之后,会执行到mParent.invalidateChild(..);
这里的mParent有两种情况,一种是有父视图ViewGroup,另一种是已经到顶层了为ViewRootImpl; - 若是ViewGroup,会执行完 invalidateChildInParent(…)之后继续调用mParent.invalidateChildInParent(…);
- 最终调用到ViewRootImpl.invalidateChildInParent(…),进而执行scheduleTraversals();
注意:这里会提前判断mWillDrawSoon局部变量值,若当前已经在执行performTraversals()遍历重绘了,那就不会调用scheduleTraversals(),也就不会发起重绘的异步消息了,但View中设置的各种状态值仍然是有效的,只是会在下次重绘时生效;
scheduleTraversals()
该方法会在多个地方被调用,比如requestLayout()/invalidate()中,而我们又会经常会看到连续调用这两个方法的情况,那这样岂不是会发起两次View树遍历重绘请求?其实是不会的,因为在scheduleTraversals()方法内设置了一个局部变量mTraversalScheduled,若先执行了requestLayout(),那此时mTraversalScheduled为false,发起一个异步消息请求重绘,并将mTraversalScheduled变量值设为true,这样接着调用invalidate()时判断mTraversalScheduled变量值已经不是false了,这样就确保了只发起一个异步重绘请求;参见下图:
performTraversals()
该方法时系统内进行View树遍历并进行页面重绘的核心方法,内部逻辑还是非常复杂的,约800行代码;老实说偶目前还未完全看懂里面的细节,中间涉及的关联变量实在太多了;但大致的主体流程还是清晰的,就是根据之前设置好的各种状态值,判断是否需要重新计算视图大小(Measure)、是否需要重新分配视图的位置(也叫布局Layout)、以及是否需要重绘视图(Draw),框架过程参见下图,其中每项的具体过程详见后面的具体描述:
以上内容若有转载,请注明出处,欢迎访问老唐的专栏http://blog.csdn.net/sfdev
《Android内核剖析》读书笔记 第13章 View工作原理【View树遍历】的更多相关文章
- Android内核剖析读书笔记
第16章 程序包管理 PackageManagerService類 PmS 目錄 16.1 包管理概述 16.2 packages.xml文件格式 16.3 包管理服務的啟動過程 16.4 應用程序的 ...
- Android内核剖析读书笔记(1)—Framework概述
一.Framework组成 1.服务端组成 a.WindowManagerService 决定各窗口的叠放次序.隐藏或者显示窗口 b.ActivityManagerService 管理应用 ...
- APUE读书笔记-第13章-守护进程
第13章 守护进程 13.1 引言 *守护进程也称精灵进程(daemon)是生存期较长的一种进程.它们常常在系统自举时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的.UNI ...
- STL源码剖析读书笔记--第四章--序列式容器
1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...
- Linux内核分析 读书笔记 (第一章、第二章)
第一章 Linux内核简介 1.1 Unix的历史 Unix很简洁,仅仅提供几百个系统调用并且有一个非常明确的设计目的. 在Unix中,所有东西都被当做文件,这种抽象使对数据和对设备的操作是通过一套相 ...
- C++ primer plus读书笔记——第13章 类继承
第13章 类继承 1. 如果购买厂商的C库,除非厂商提供库函数的源代码,否则您将无法根据自己的需求,对函数进行扩展或修改.但如果是类库,只要其提供了类方法的头文件和编译后的代码,仍可以使用库中的类派生 ...
- JavaScript高级程序设计第三版-读书笔记(1-3章)
这是我第一次用markdown,也是我第一次在网上记录我自己的学习过程. 第一章 JavaScript主要由以下三个不同的部分构成 ECMAScript 提供核心语言功能 DOM 提供访问 ...
- 《Android内核剖析》读书笔记 第13章 View工作原理【View重绘过程】
计算视图大小的过程(Measure) 视图大小,准确的来说应该是指视图的布局大小:我们在layout.xml中为每个UI控件设置的layout_width/layout_height两个属性被用来设置 ...
- $《第一行代码:Android》读书笔记——第13章 Android高级技巧
(一)全局获取Context 1.创建ApplicationUtil类继承自Application类: public class ApplicationUtil extends Application ...
随机推荐
- fzu 1911 C. Construct a Matrix
C. Construct a Matrix Time Limit: 1000ms Case Time Limit: 1000ms Memory Limit: 32768KB Special Judge ...
- 修改XPMenu让ToolButton在Down=True时正确显示
XPMenu是一个不错的程序界面效果控件,但它也存在不少不足之处.我最近又对它作了一点修改. 原因是我在程序里有一个ToolButton,其Style=tbsButton,当Down=True时,XP ...
- 如何使用Gmail的别名功能?
Gmail真的是一个很好的邮箱,一直是我的最爱!它有一个很独特的功能-别名,用这个功能,我们就可以把一个 邮箱当成很多个邮箱来使用了! 1.Gmail 不提供传统别名 ,但是你可以收到发送到 your ...
- 显示出eclipse文件层次
看到图片中右边那个倒三角型符号没, 点一下,弹出个菜单,选package presentation->hierarachial 文件目录结构 flat 是包结构
- linux shell中的单引号与双引号的区别(看完就不会有引号的疑问了)(转)
tips: ============================= IFS - LINUX字段分隔符,内部字段分隔符 IFS(Internal Field Seperator)在Linux的she ...
- POJ 1466 最大独立集入门
题意:n个学生,给你每个学生浪漫的学生学号(男女之间浪漫),问你找出一个最大的集合保证集合内的任意两个学生之间没有相互浪漫关系,输出最大集合的人数. 注意:这里的浪漫边是双向的,如果1对2浪漫, 那么 ...
- Swift - 列表项尾部附件点击响应(感叹号,箭头等)
列表单元格尾部可以添加各种样式的附件,如感叹号,三角箭头等.而且点击内容区域与点击附件的这两个响应事件是不同的,这样可以方便我们实现不同的功能(比如点击内容则查看详情,点击感叹号则编辑) 1 2 3 ...
- Android学习笔记(十三)——碎片(一)
碎片 碎片可看作第二种形式的活动,能够创建碎片来包括视图. 碎片总是嵌入在活动中,一般有两种常见形式: 1.碎片A和碎片B分别处于不同的活动中,当选择碎片A中的某一项时,触发碎片B启动: 2.碎片A和 ...
- Coins (poj 1742 && hdu 2844 DP)
Language: Default Coins Time Limit: 3000MS Memory Limit: 30000K Total Submissions: 30047 Accepte ...
- 【deep learning学习笔记】注释yusugomori的LR代码 --- LogisticRegression.h
继续看yusugomori的代码,看逻辑回归.在DBN(Deep Blief Network)中,下面几层是RBM,最上层就是LR了.关于回归.二类回归.以及逻辑回归,资料就是前面转的几篇.套路就是设 ...