android 二次绘制 layout,View的三次measure,两次layout和一次draw

我在《Android视图结构》这篇文章中已经描述了Activity,Window和View在视图架构方面的关系。前天,我突然想到为什么在setContentView中能够调用findViewById函数?View那时不是还没有被加载,测量,布局和绘制啊。然后就搜索了相关的条目,发现findViewById只需要在inflate结束之后就可以。于是,我整理了Activity生命周期和View的生命周期的关系,并再次做一下总结。

为了节约你的时间,本篇文章的主要内容为:

Activity的生命周期和它包含的View的生命周期的关系

Activity初始化时View为什么会三次measure,两次layout但只一次draw?

ViewRoot的初始化过程

Activity的生命周期和View的生命周期

我通过一个简单的demo来验证Activity生命周期方法和View的生命周期方法的调用先后顺序。请看如下截图

bVETOH?w=854&h=336

在onCreate函数中,我们通常都调用setContentView来设置布局文件,此时Android系统就会读取布局文件,但是视图此时并没有加载到Window上,并且也没有进入自己的生命周期。

只有等到Activity进入resume状态时,它所拥有的View才会加载到Window上,并进行测量,布局和绘制。所以我们会发现相关函数的调用顺序是:

onResume(Activity)

onPostResume(Activity)

onAttachedToWindow(View)

onMeasure(View)

onMeasure(View)

onLayout(View)

onSizeChanged(View)

onMeasure(View)

onLayout(View)

onDraw(View)

大家会发现,为什么onMeasure先调用了两次,然后再调用onLayout函数,最后还有在依次调用onMeasure,onLayout和onDraw函数呢?

ViewGroup的measure

大家应该都知道,有些ViewGroup可能会让自己的子视图测量两次。比如说,父视图先让每个子视图自己测量,使用View.MeasureSpec.UNSPECIFIED,然后在根据每个子视图希望得到的大小不超过父视图的一些限制,就让子视图得到自己希望的大小,否则就用其他尺寸来重新测量子视图。这一类的视图有FrameLayout,RelativeLayout等。

在《Android视图结构》中,我们已经知道Android视图树的根节点是DecorView,而它是FrameLayout的子类,所以就会让其子视图绘制两次,所以onMeasure函数会先被调用两次。

// FrameLayout的onMeasure函数,DecorView的onMeasure会调用这个函数。

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int count = getChildCount();

.....

for (int i = 0; i < count; i++) {

final View child = getChildAt(i);

if (mMeasureAllChildren || child.getVisibility() != GONE) {

measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

......

}

}

........

count = mMatchParentChildren.size();

if (count > 1) {

for (int i = 0; i < count; i++) {

........

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

}

}

你以为到了这里就能解释通View初始化时的三次measure,两次layout却只一次draw吗?那你就太天真了!我们都知道,视图结构中不仅仅是DecorView是FrameLayout,还有其他的需要两次measure子视图的ViewGroup,如果每次都导致子视图两次measure,那效率就太低了。所以View的measure函数中有相关的机制来防止这种情况。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

......

// 当FLAG_FORCE_LAYOUT位为1时,就是当前视图请求一次布局操作

//或者当前当前widthSpec和heightSpec不等于上次调用时传入的参数的时候

//才进行从新绘制。

if (forceLayout || !matchingSize &&

(widthMeasureSpec != mOldWidthMeasureSpec ||

heightMeasureSpec != mOldHeightMeasureSpec)) {

......

onMeasure(widthMeasureSpec, heightMeasureSpec);

......

}

......

}

源码看到这里,我几乎失望的眼泪掉下来!没办法,只能再想其他的方法来分析这个问题。

断点调试大法好

为了分析函数调用的层级关系,我想到了断点调试法。于是,我果断在onMeasure和onLayout函数中设置了断点,然后进行调试。

bVETOP?w=1137&h=676

在《Android视图架构》一文中,我们知道ViewRoot是DecorView的父视图,虽然它自己并不是一个View,但是它实现了ViewParent的接口,Android正是通过它来实现整个视图系统的初始化工作。而它的performTraversals函数就是视图初始化的关键函数。

对比两次onMeasure被调用时的函数调用帧,我们可以轻易发现ViewRootImpl的performTraversals函数中直接和间接的调用了两次performMeasure函数,从而导致了View最开始的两次measure过程。

然后在相同的performTraversals函数中会调用performLayout函数,从而导致View进行一轮layout过程。

但是为什么这次performTraversals并没有触发View的draw过程呢?反而是View又将重新进行一轮measure,layout过程之后才进行draw。

两次performTraversals

通过断点调试,我们发现在View初始化的过程中,系统调用了两次performTraversals函数,第一次performTraversals函数导致了View的前两次的onMeasure函数调用和第一次的onLayout函数调用。后一次的performTraversals函数导致了最后的onMeasure,onLayout,和onDraw函数的调用。但是,第二次performTraversals为什么会被触发呢?我们研究一下其源码就可知道。

private void performTraversals() {

......

boolean newSurface = false;

//TODO:决定是否让newSurface为true,导致后边是否让performDraw无法被调用,而是重新scheduleTraversals

if (!hadSurface) {

if (mSurface.isValid()) {

// If we are creating a new surface, then we need to

// completely redraw it. Also, when we get to the

// point of drawing it we will hold off and schedule

// a new traversal instead. This is so we can tell the

// window manager about all of the windows being displayed

// before actually drawing them, so it can display then

// all at once.

newSurface = true;

.....

}

}

......

if (!cancelDraw && !newSurface) {

if (!skipDraw || mReportNextDraw) {

......

performDraw();

}

} else {

if (viewVisibility == View.VISIBLE) {

// Try again

scheduleTraversals();

} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {

for (int i = 0; i < mPendingTransitions.size(); ++i) {

mPendingTransitions.get(i).endChangingAnimations();

}

mPendingTransitions.clear();

}

}

......

}

由源代码可以看出,当newSurface为真时,performTraversals函数并不会调用performDraw函数,而是调用scheduleTraversals函数,从而再次调用一次performTraversals函数,从而再次进行一次测量,布局和绘制过程。

我们由断点调试可以轻易看到,第一次performTraversals时的newSurface为真,而第二次时是假。

bVETOS?w=813&h=512

总结

虽然我已经通过源码得知View初始化时measure三次,layout两次,draw一次的原因,但是Android系统设计时,为什么要将整个初始化过程设计成这样?我却还没有明白,为什么当Surface为新的时候,要推迟绘制,重新进行一轮初始化,这些可能都要涉及到Surface的相关内容,我之后要继续学习相关内容!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/357530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

盘点关于程序员的那些经典案例

深度剖析几个经典话题&#xff0c;以图文的形式展现&#xff0c;好好看图。 目录 1. 2014互联网职场薪酬报告&#xff01;你拖后腿了吗&#xff1f; 2. 月薪8K程序员现身说法&#xff1a;男人就该默默做技术&#xff01; 3.项目建设 4. 2014年亿级用户下的新浪微博平台架构…

html中文字过长 自动隐藏,css 实现文字过长自动隐藏功能

单行overflow: hidden;text-overflow: ellipsis;white-space: nowrap;多行(兼容各个浏览器)//通过覆盖最后几个字的形式p{position:relative;line-height:1.4em;height:4.2em;/* 3 倍line-height 多少倍就是多少行*/overflow:hidden;}.p::after {content:"...";font-…

使用Xtext为Eclipse和IntelliJ开发DSL

在这篇文章中&#xff0c;我们将看到如何开发一种简单的语言。 我们的目标是&#xff1a; 语言的解析器 IntelliJ的编辑器 。 编辑器应具有语法突出显示&#xff0c;验证和自动完成功能 我们还将免费提供Eclipse和Web编辑器的编辑器 &#xff0c;但请包含您的兴奋之处&#…

搬家后第一次缴电费,查询客户编号的解决办法

最近搬家后&#xff0c;发现家里停电了&#xff0c;不知道客户编号&#xff0c;想通过支付宝生活缴费模块充值。 解决办法。 1.走到电表跟前&#xff0c;连续按下电表白色按钮4-5下。会出现一个四位数的阿拉伯数字。比如&#xff1a;0088。取&#xff0c;后两位88。 2.再按一下…

html字符实体标签语法,HTML字符实体与文本格式化标签

HT环行进端处触码通法果泉位可近境其行框理发ML 字符实体/HTML 中的预留字符必须被替换为字符实体&#xff0c;一些在键盘上找不到的字符也可以使用字符实体来览页些求时是过解些这确如目前例总站回广随能4果泉时标配使能幻近器面实的我是接,前些模小架端如结的事告机对8和水兼…

ArcGIS API for Silverlight地图加载众多点时,使用Clusterer解决重叠问题

ArcGIS API for Silverlight地图加载众多点时&#xff0c;使用Clusterer解决重叠问题 原文:ArcGIS API for Silverlight地图加载众多点时&#xff0c;使用Clusterer解决重叠问题 问题&#xff1a;如果在地图上加载成百上千工程点时&#xff0c;会密密麻麻&#xff0c;外观不是很…

html 文本框 自动拼接,HTML 中table的结构以及拼接

表格基本上有如下几个标签构成:(1).(2).标签用来创建表格的行。(3).标签用来创建表头单元格。 t-head(4).标签用来创建tr行中的单元格。(5).标签用来创建标题。(6).标签用来创建表格的表头。 (一个table只能有一个)(7).标签用来创建表格的主体部分。(8).标签用来创建表格的页…

谈谈JAVA工程狮面试中经常遇到的面试题目------什么是MVC设计模式

作为一名java工程狮&#xff0c;大家肯定经历过很多面试&#xff0c;但每次几乎都会被问到什么是MVC设计模式&#xff0c;你是怎么理解MVC的类似这样的一系列关于MVC的问题。 【出现频率】 【关键考点】 MVC的含义MVC的结构 【考题分析】  在java Web开发中&#xff0c;存在两…

mvc 一般注释_使用带有注释和JQuery的Spring MVC 3的Ajax

mvc 一般注释与Ajax一起工作对我来说一直很有趣&#xff01; 是不是 &#xff1f; 我将使您轻松将Ajax与Spring MVC 3和JQuery结合使用。 这篇文章将向您说明如何在工业编码的现实生活中使用Ajax。 和往常一样&#xff0c;我们将在Spring MVC 3框架中以Ajax的实际示例为例&…

html中el表达式遍历list,EL表达式在JS中取出来打印[object HTMLDivElement]的问题

今天做项目的时候,要在JS中获取请求参数中的 值,想直接用 ${param.tabName}获取,结果console.debug()打印出来,居然是 [object HTMLDivElement] 类型. 导致无法获取真正的值,原因可能是因为JQ默认把 这个值进行了封装,封装成 HTMLDivElement 对象,导致出问题. 解决办法,就是告…

孙叫兽:我所认为的领导力!

回归领导力的本质&#xff0c;观察反思自己日常的领导力行为&#xff0c;在不断的学习中找到属于自己的最佳答案... 目录 重塑领导力 管理&#xff0c;到底是管人还是管事&#xff1f; 领导力唯一的准则是没有准则 改变自己就能改变公司&#xff1f; 成功的战略10%制定90%执…

WildFly管理控制台已更新–请求反馈

红帽JBoss企业应用程序平台&#xff08;EAP&#xff09;和WildFly具有共生关系 。 简而言之&#xff0c;红帽JBoss企业应用程序平台&#xff08;JBoss EAP&#xff09;保留了WildFly社区项目&#xff08;以前称为JBoss Application Server&#xff09;的所有创新。 但是只有JBo…

win7远程多用户登录此计算机无法,win7如何实现远程桌面多用户登录|win7实现多用户登录远程桌面的方法...

Win7系统自带有远程桌面功能&#xff0c;开启远程桌面可以控制其他电脑&#xff0c;一般远程登录桌面时&#xff0c;即使登录的是不同的管理账号&#xff0c;还是会把远程登录的人给记下来&#xff0c;不同的账号只能同时存在一个会话窗。那么win7如何实现远程桌面多用户登录&a…

饿了么薅羊毛时刻正式开启

随着信息化时代的来临&#xff0c;我们的生活方式更加的方便快捷。随着外卖行业的发展&#xff0c;我们不用踏出家门就可以吃到热乎的饭菜了。我们网上点餐都是通过外卖app来进行点餐的&#xff0c;现在的外卖app主要要饿了么、美团外卖等等。有时候外卖平台也会推出一些优惠活…

软件测试 实验一

一、Junit&#xff0c; hamcrest 和 eclemma 的安装和使用 通过右击项目里build path 里的 add external archives...来添加Junit包和hamcrest包。运行Junit时&#xff0c;只需在测试用例上右击run as->Junit test,即可对要测试的函数进行测试。 eclemma压缩包我是通过在线安…

如何开发高度可定制的产品

您是否听说过&#xff1a;“我们非常喜欢您的产品……除了一些小细节”。 然后&#xff0c;CIO推出了一系列其他“必备”要求的清单&#xff0c;其中有数百个要求添加到您的惊人产品中。 您是否听说过&#xff0c;甚至说过&#xff1a;“团队&#xff0c;我们即将签署一份利润丰…

前端使用正则表达式从接口地址栏取值并将对应的值展示在页面上

业务场景&#xff0c;APP分享出链接&#xff0c;通过get请求接口方式&#xff0c;展示对应的字段。 需求图&#xff1a; 获取某单号 var name"";//姓名var idNo"";//证件号var applicationNogetParams("applicationNo");//号码window.onload fu…

科学计算机看电量,解密:关于手机电量为1%是如何科学的算出来的?

本文的话题也许是很多人的疑问&#xff0c;对于手机显示电量是怎么推算出来的&#xff0c;到底显示1%的时候还有没有电呢&#xff1f;这是一个直击灵魂的问题——有时候手机最后1%的电能用很久&#xff0c;有时候却只能用一瞬间。给人留下这个印象&#xff0c;有一些心理层面的…

node源码详解(四) —— js代码如何调用C++的函数

本作品采用知识共享署名 4.0 国际许可协议进行许可。转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource4 本博客同步在https://cnodejs.org/topic/56ed249356d74f3d3624b3ff 本博客同步在http://www.cnblogs.com/papertree/p/5285705.html 上面讲到node调用Scrip…

EasyConnect安装使用教程

easyconnect电脑版是一款为企业提供的移动信息化办公软件&#xff0c;这款软件可以让公司经常出差的人员能在公司范围外使用公司的内网系统和相关应用。软件支持移动和pc平台&#xff0c;不管是在电脑上还是手机上使用都非常方便&#xff0c;easyconnect电脑版便捷性和安全性使…