View状态分类

在View视图中定义了多种和界面效果相关的状态,比如拥有焦点Focused、按下Pressed等,不同的状态一般会显示不同的界面效果,而且视图状态会随着用户的操作而改变,一般通过xml文件中selector来申明各种状态下使用的背景图;所有的状态码位于StateListDrawable中,常用的状态码包括:

  1. enable:当前View是否可用,开发者可以通过setEnable()改变,他完全由开发者控制;
    当状态为不可用时,View将不会响应任何事件;
  2. focused:当前View是否正拥有焦点,一个窗口中只能有一个View拥有焦点,一般随用户操作而动态改变;
    该状态主要是针对按键的,因为所有的按键消息都将派发给focused视图;
  3. pressed:当前View是否正被按下,主要是针对触摸消息的,一般当用户按下时视图会有一个明显的变化,也是随用户操作而动态改变;
  4. 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标识,然后根据所有带该标识的视图边界一起确定最终要重绘的矩形区块,这里面会涉及到不同坐标体系间的换算,可以参见下图:

代码的具体执行过程是:

  1. View.invalidate()中设置必要的状态位标识之后,会执行到mParent.invalidateChild(..);
    这里的mParent有两种情况,一种是有父视图ViewGroup,另一种是已经到顶层了为ViewRootImpl;
  2. 若是ViewGroup,会执行完 invalidateChildInParent(…)之后继续调用mParent.invalidateChildInParent(…);
  3. 最终调用到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树遍历】的更多相关文章

  1. Android内核剖析读书笔记

    第16章 程序包管理 PackageManagerService類 PmS 目錄 16.1 包管理概述 16.2 packages.xml文件格式 16.3 包管理服務的啟動過程 16.4 應用程序的 ...

  2. Android内核剖析读书笔记(1)—Framework概述

    一.Framework组成 1.服务端组成 a.WindowManagerService     决定各窗口的叠放次序.隐藏或者显示窗口 b.ActivityManagerService   管理应用 ...

  3. APUE读书笔记-第13章-守护进程

    第13章 守护进程 13.1 引言 *守护进程也称精灵进程(daemon)是生存期较长的一种进程.它们常常在系统自举时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的.UNI ...

  4. STL源码剖析读书笔记--第四章--序列式容器

    1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...

  5. Linux内核分析 读书笔记 (第一章、第二章)

    第一章 Linux内核简介 1.1 Unix的历史 Unix很简洁,仅仅提供几百个系统调用并且有一个非常明确的设计目的. 在Unix中,所有东西都被当做文件,这种抽象使对数据和对设备的操作是通过一套相 ...

  6. C++ primer plus读书笔记——第13章 类继承

    第13章 类继承 1. 如果购买厂商的C库,除非厂商提供库函数的源代码,否则您将无法根据自己的需求,对函数进行扩展或修改.但如果是类库,只要其提供了类方法的头文件和编译后的代码,仍可以使用库中的类派生 ...

  7. JavaScript高级程序设计第三版-读书笔记(1-3章)

    这是我第一次用markdown,也是我第一次在网上记录我自己的学习过程. 第一章 JavaScript主要由以下三个不同的部分构成 ECMAScript   提供核心语言功能 DOM     提供访问 ...

  8. 《Android内核剖析》读书笔记 第13章 View工作原理【View重绘过程】

    计算视图大小的过程(Measure) 视图大小,准确的来说应该是指视图的布局大小:我们在layout.xml中为每个UI控件设置的layout_width/layout_height两个属性被用来设置 ...

  9. $《第一行代码:Android》读书笔记——第13章 Android高级技巧

    (一)全局获取Context 1.创建ApplicationUtil类继承自Application类: public class ApplicationUtil extends Application ...

随机推荐

  1. MFC界面相关(彩色工具栏)

    MFC工具栏控件 创建工具栏步骤: (1)在Resource View中插入新toolbar (2)在toolbar上双击增加按钮,更改ID为ID_BUTTON,编译后在resource.h中即可看到 ...

  2. CloudStack搭建KVM环境

    软件环境:agent:CentOS 6.3,minimal安装,CPU启用VT management server:CentOS 6.3,minimal安装 存储:CentOS 6.3 搭建在mana ...

  3. pig对null的处理(实际,对空文本处理为两种取值null或‘’)

    pig对文本null的处理非常特殊.会处理成两种null,还会处理成''这样的空值. 比方,读name,age,sex日志信息.name取值处理,假设记录为".,,"这样,会将na ...

  4. Delphi Socket的最好项目——FastMsg IM(还有一些IM控件),RTC,RO,Sparkle等等,FileZilla Client/Server,wireshark,NSClient

    https://www.nsclient.org/nsclient/ 好好学习,天天向上

  5. 关于IOCP,某些地方就是不让人彻底舒服(WSASend重叠post数据)

    开门见山,直接就事论事. 假如有这么一个基于IOCP模型的Server,这个Server提供的所有服务中有这么一种服务……文件下载,我们再假设Server端存有一个20G的文件,客户端这时发送一个请求 ...

  6. 【Demo 0003】Java基础-数组

    本章学习要点:       1.  了解数组的基本概念:       2.  掌握数组使用方法:  一.数组的基本概念     1.  数组定义:              同一数据类型数据的集合,在 ...

  7. Nancy.Host的Web应用

    Nancy.Host实现脱离iis的Web应用 本篇将介绍如何使用Nancy.Host实现脱离iis的Web应用,在开源任务管理平台TaskManagerV2.0代码里面已经使用了Nancy.Host ...

  8. 《powershell 的版本号所引起的载入 FSharp 编译器问题》基本解决

    <powershell 的版本号所引起的载入 FSharp 编译器问题>基本解决 1.FSharp.Core.dll.不光要 Add-Type,还要在编译中引用.可是,在 VS2012 的 ...

  9. hdu4493 Tutor

    Tutor Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) Total Submiss ...

  10. ArcGIS多面体(multipatch)解析——引

    多面体(multipatch)结构在ArcGIS数据结构中是与点.线.面平行的一种数据结构,对于ArcGIS三维来说是一个很核心的结构,有了它,ArcGIS平台才可以灵活的描述规则和不规则的三维实体. ...