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,一经查实,立即删除!

相关文章

mule esb 集成_集成框架比较– Spring集成,Mule ESB或Apache Camel

mule esb 集成公司之间的数据交换增加了很多。 必须集成的应用程序数量也增加了。 这些接口使用不同的技术&#xff0c;协议和数据格式。 然而&#xff0c;这些应用程序的集成应以标准化的方式建模&#xff0c;有效实现并由自动测试支持 。 JVM环境中提供了三个可满足这些要求的…

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

深度剖析几个经典话题&#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-…

写博客和生产api的工具

前言&#xff1a; .不要乱管闲事 写博客&#xff1a; 印象中是叫writeAlive之类的工具&#xff1b; 生产api的&#xff1a; 似乎叫hignLight的工具&#xff1b;转载于:https://www.cnblogs.com/zhangzs000/p/5273346.html

使用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和水兼…

JavaScript学习总结(思维导图篇)

不管是前端开发还是后端开发,js一直担任着重要的角色。需要深刻理解,才能把工作做得更加出色。 目录 JavaScript BOM对象 JavaScript Dcoument对象 JavaScript 事件处理 JavaScript变量 JavaScript函数基础

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).标签用来创建表格的页…

大学4年毕业后我是如何还清的助学贷款。

一般的农村大学生,大学期间采用助学贷款的方式修完大学的四年。如何在一两年内还清?这个利息又是如何计算的呢, 目录 我大学欠了多少钱? 大学毕业后要还多少钱? 这个利息如何计算?

谈谈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…

web.xml上监听器作用

<!--Spring ApplicationContext 载入 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 使Spring支持request与session的scope,如:<bean id"log…

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

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

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

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

500个运营工具大全,速度收藏!!!

500个运营工具大全! 目录 综合性平台: 运营、营销、广告类型平台: 创业资讯类