Android中mesure过程详解

我们在编写layout的xml文件时会碰到layout_width和layout_height两个属性,对于这两个属性我们有三种选择:赋值成具体的数值,match_parent或者wrap_content,而measure过程就是用来处理match_parent或者wrap_content,假如layout中规定所有View的layout_width和layout_height必须赋值成具体的数值,那么measure其实是没有必要的,但是google在设计Android的时候考虑加入match_parent或者wrap_content肯定是有原因的,它们会使得布局更加灵活。

  首先我们来看几个关键的函数和参数:
      1public final void measue(int widthMeasureSpec, int heightMeasureSpec);2protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);3、protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)4、protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)5、protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)

接着我们来看View类中measure和onMeasure函数的源码:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||widthMeasureSpec != mOldWidthMeasureSpec ||heightMeasureSpec != mOldHeightMeasureSpec) {// first clears the measured dimension flagmPrivateFlags &= ~MEASURED_DIMENSION_SET;if (ViewDebug.TRACE_HIERARCHY) {ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);}// measure ourselves, this should set the measured dimension flag backonMeasure(widthMeasureSpec, heightMeasureSpec);// flag not set, setMeasuredDimension() was not invoked, we raise// an exception to warn the developerif ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {throw new IllegalStateException("onMeasure() did not set the"+ " measured dimension by calling"+ " setMeasuredDimension()");}mPrivateFlags |= LAYOUT_REQUIRED;}mOldWidthMeasureSpec = widthMeasureSpec;mOldHeightMeasureSpec = heightMeasureSpec;}

由于函数原型中有final字段,那么measure根本没打算被子类继承,也就是说measure的过程是固定的,而measure中调用了onMeasure函数,因此真正有变数的是onMeasure函数,onMeasure的默认实现很简单,源码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}

onMeasure默认的实现仅仅调用了setMeasuredDimension,setMeasuredDimension函数是一个很关键的函数,它对View的成员变量mMeasuredWidth和mMeasuredHeight变量赋值,而measure的主要目的就是对View树中的每个View的mMeasuredWidth和mMeasuredHeight进行赋值,一旦这两个变量被赋值,则意味着该View的测量工作结束。

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {mMeasuredWidth = measuredWidth;mMeasuredHeight = measuredHeight;mPrivateFlags |= MEASURED_DIMENSION_SET;}

对于非ViewGroup的View而言,通过调用上面默认的measure——>onMeasure,即可完成View的测量,当然你也可以重载onMeasure,并调用setMeasuredDimension来设置任意大小的布局,但一般不这么做,因为这种做法太“专政”,至于为何“专政”,读完本文就会明白。

对于ViewGroup的子类而言,往往会重载onMeasure函数负责其children的measure工作,重载时不要忘记调用setMeasuredDimension来设置自身的mMeasuredWidth和mMeasuredHeight。如果我们在layout的时候不需要依赖子视图的大小,那么不重载onMeasure也可以,但是必须重载onLayout来安排子视图的位置,这在下一篇博客中会介绍。

再来看下measue(int widthMeasureSpec, int heightMeasureSpec)中的两个参数, 这两个参数分别是父视图提供的测量规格,当父视图调用子视图的measure函数对子视图进行测量时,会传入这两个参数,通过这两个参数以及子视图本身的LayoutParams来共同决定子视图的测量规格,在ViewGroup的measureChildWithMargins函数中体现了这个过程,稍后会介绍。

MeasureSpec参数的值为int型,分为高32位和低16为,高32位保存的是specMode,低16位表示specSize,specMode分三种:

1、MeasureSpec.UNSPECIFIED,父视图不对子视图施加任何限制,子视图可以得到任意想要的大小;

2、MeasureSpec.EXACTLY,父视图希望子视图的大小是specSize中指定的大小;

3、MeasureSpec.AT_MOST,子视图的大小最多是specSize中的大小。

以上施加的限制只是父视图“希望”子视图的大小按MeasureSpec中描述的那样,但是子视图的具体大小取决于多方面的。

ViewGroup中定义了measureChildren, measureChild, measureChildWithMargins来对子视图进行测量,measureChildren内部只是循环调用measureChild,measureChild和measureChildWithMargins的区别就是是否把margin和padding也作为子视图的大小,我们主要分析measureChildWithMargins的执行过程:

 protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin+ heightUsed, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);} 

总的来看该函数就是对父视图提供的measureSpec参数进行了调整(结合自身的LayoutParams参数),然后再来调用child.measure()函数,具体通过函数getChildMeasureSpec来进行参数调整,过程如下:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:if (childDimension >= 0) {// Child wants a specific size... let him have itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should// beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how// big it should beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;}break;}return MeasureSpec.makeMeasureSpec(resultSize, resultMode);} 

getChildMeasureSpec的总体思路就是通过其父视图提供的MeasureSpec参数得到specMode和specSize,并根据计算出来的specMode以及子视图的childDimension(layout_width和layout_height中定义的)来计算自身的measureSpec,如果其本身包含子视图,则计算出来的measureSpec将作为调用其子视图measure函数的参数,同时也作为自身调用setMeasuredDimension的参数,如果其不包含子视图则默认情况下最终会调用onMeasure的默认实现,并最终调用到setMeasuredDimension,而该函数的参数正是这里计算出来的。

1)不论父View 的specMode是EXACTLY、UNSPECIFIED、AT_MOST的哪一种,如果子view在xml文件里面把layout_width或者layout_height这是了具体
的dimen值、或者在代码里调用相应的方法为view的宽度或者高度设置了具体的值,那么此时widthSpec的mode 或者heightSpec的mode就是EXACLTY,size就等于layout_width或者
layout_height的值。此时子view的specMode值不受父View的specMode的影响。

2)当父View的specMode为EXACTLY的时候:父View强加给子View一个确切的大小,有如下两种情况
2.1)子View的layout_width或者layout_height设置为MATCH_PARENT的时候,子View的specMode为EXACTLY
2.2)子View的layout_width或者layout_height设置为 WRAP_CONTENT的时候,子View的specMode为AT_MOST
2.3)不论子view为match_parent或者wrap_content,resultSize都等于父类的size.

3)当父view的specMode为AT_MOST的时候:父View强加给一个最大的size给子view,最大的size也就是父view的size
3.1)此时不论子view的为match_parent或者wrap_content,子view的specMode都为AT_MOST
3.2)resultSize的大小被设置为父view的大小

4)当父view的specMode为UNSPECIFIED的时候:
4.1) 此时不论子view的为match_parent或者wrap_content,子view的specMode都为UNSPECIFIED
4.3)此时reusltSize = 0

5)从对UNSPECIFIED分析可以知道,在1)情况之外的情况下,要是想让子view自己决定自己的宽度或者高度的大小,MeasureSpec.makeMeasureSpec(0,UNSPECIFIED)这么调用来获取宽度和高度
的spec

6)根据方法的参数以及方法的实现可以知道,视图的大小是由父视图和子视图共同决定的,前提是子视图没有设置具体的dimen值

总结:从上面的描述看出,决定权最大的就是View的设计者,因为设计者可以通过调用setMeasuredDimension决定视图的最终大小,例如调用setMeasuredDimension(100, 100)将视图的mMeasuredWidth和mMeasuredHeight设置为100,100,那么父视图提供的大小以及程序员在xml中设置的layout_width和layout_height将完全不起作用,当然良好的设计一般会根据子视图的measureSpec来设置mMeasuredWidth和mMeasuredHeight的大小,已尊重程序员的意图。

通过以下方法可以强制计算当前view的宽高,原因不明

int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);view.measure(w, h);mChildHeight = view.getMeasuredHeight();mChildWidth = view.getMeasuredWidth();

参考链接

获取view的高度和宽度(在onCreate方法中) - Listening_music的专栏 - 博客频道 - CSDN.NET

本文主要参考

Android中mesure过程详解 – - 那些人追过的年 - 博客园

MeasureSpec学习 - 转 - yuhailong626的专栏 - 博客频道 - CSDN.NET

MeasureSpec的简单说明 - 凌云 - 博客频道 - CSDN.NET

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

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

相关文章

黑科技揭秘 | 阿里云“天空物联网”连接范围如何达到700平方公里

来源:阿里云还记得前不久在2018杭州云栖大会上,由飞在天上的飞艇、地下基站共同搭建的阿里云“天空物联网”吗?它实现从地面40000米高空到地下20米的覆盖,并持续为大会服务。体现了持续加码的阿里巴巴物联网战略。在飞艇背后&…

JAVA-用栈机制实现单词逆序排列

就是IO那一段还没学到。 之前的PUCH,POP,STRING和CHAR的关系搞得懂了。 学到一个定位STRING当中CHAR的转换函数。 char String.charAt(x) 1 import java.io.*;2 3 4 class stackString5 {6 private int maxSize;7 private char[] stackX;8 private int top;9 …

Android自定义实现FlowLayout

实现FlowLayout 何为FlowLayout,如果对Java的Swing比较熟悉的话一定不会陌生,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行。有点所有的控件都往左飘的感觉,第一…

【重磅】马斯克遇终极麻烦:被起诉欺诈罪 或丢掉CEO职位 特斯拉暴跌约13%

参考:CNBC、Bloomberg编译:网易智能编辑:丁广胜参与:小小美国当地时间周四,法庭的文件显示特斯拉电动汽车公司首席执行官伊隆马斯克(Elon Musk)已被美国证券交易委员会(SEC)以欺诈罪起诉。与特斯拉关系密切的消息人士透…

《麻省理工科技评论》:2018年18大科技趋势,2017年7大失败技术

来源:科技周摘要:2018 年伊始,许多科技大势仍在继续,正如比尔盖茨所说,“大多数人高估了某种技术的短期价值,低估了其长期价值。”同样,大多数的年度预测会高估了一年内一些事件发生的可能性&am…

Android实现支持缩放平移图片

本文主要用到了以下知识点 MatrixGestureDetector 能够捕捉到长按、双击ScaleGestureDetector 用于检测缩放的手势 自由的缩放 需求:当图片加载时,将图片在屏幕中居中;图片宽或高大于屏幕的,缩小至屏幕大小;自由对图…

放麦子

题意: 国际象棋,一共64个方格,第一个格子里放一粒麦子,第二个放2粒,第三个放4粒,第四个放8粒。。。。。(后面的数字是前面的两倍) 求放满64个格子,一共需要多少粒麦子。 …

Material Design风格登录注册

本文实现了以下功能 完整的代码和样例托管在Github当接口锁定时,防止后退按钮显示在登录Activity 上。自定义 ProgressDialog来显示加载的状态。符合材料设计规范。悬浮标签(floating labels)(来自设计支持库)用户表单…

英特尔反驳质疑:芯片供应充足、10nm量产没问题

来源:华尔街见闻摘要:英特尔称2018年会将资本支出增加10亿美元,至总额创纪录的150亿美元;CEO称,个人电脑需求意外回升,但有足够供应满足市场,有望达成全年营收目标,股价涨近4%。竞争…

RecyclerView拖拽排序和滑动删除实现

效果图 如何实现 那么是如何实现的呢?主要就要使用到ItemTouchHelper ,ItemTouchHelper 一个帮助开发人员处理拖拽和滑动删除的实现类,它能够让你非常容易实现侧滑删除、拖拽的功能。 实现的代码非常简单我们只需要两步: 实例化…

马斯克刚刚宣布辞去特斯拉董事会职务,仍然担任CEO

来源:大数据文摘编译:蒋宝尚据悉,当地时间周六,马斯克辞去特斯拉董事会主席一职,并且支付2000万美元罚款。以表示对美国证券交易委员会(SEC)指控的回应。SEC的指控来源于马斯克8月7日的一篇推文。推文中,他…

JavaScript 实现 GriwView 单列全选

在 GridView 里有一系列的 Checkbox ,要实现对其全选或全不选。开始在网上找了,但是参考的代码会全选 GridView 里所有的 Checkbox ,而我要的是单列全选。如图: 审核和权限是要分开的。 我自己写了 JavaScript 代码,贴…

自然语言处理(NLP)前沿进展报告

来源:专知摘要:2018年9 月 9 日-14 日,DeepMind主办的Deep Learning Indaba 2018 大会在南非斯泰伦博斯举行。会上,斯坦陵布什大学Herman Kamper和AYLIEN的Sebastian Ruder等专家做了《自然语言处理前言进展》的报告。报告首先探讨…

Android下利用Bitmap切割图片

在自己自定义的一个组件中由于需要用图片显示数字编号,而当前图片就只有一张,上面有0-9是个数字,于是不得不考虑将其中一个个的数字切割下来,需要显示什么数字,只需要组合一下就好了。 下面是程序的关键代码&#xff…

两种不同的人工智能观:工程观点和科学观点

来源:人机与认知实验室作者按:人工智能是作为工程还是科学,这个问题是一个很严肃的问题,如果从工程技术角度来看人工智能,那么认知科学尤其是认知心理学、神经生理学对人类这样的生物智能的原理性认识,就不…

BaseActivity与BaseFragment的封装

这篇博客主要是从BaseActivity与BaseFragment的封装开始,总结我们在实战开发中关于Fragment的注意事项以及心得体会。 先看以下效果图: 这里模拟的是用户登录模块,你可能会说,很普通的效果嘛,这有啥。嘿嘿&#xff0c…

学界 | 史上最强GAN图像生成器,Inception分数提高两倍

来源:Openreview,机器之心摘要:ICLR 2019 大会即将在明年 5 月 6 日于美国举行,9 月 27 日论文提交截止时间已过。本次大会共接收到了 1591 篇论文的投稿,数量相较今年的 1000 篇提升了近 60%。在双盲评审时,人们可以在…

一图理解腾讯本次组织结构重大变革的方向和目的

作者:刘锋 互联网进化论作者 计算机博士2018年9月30日,腾讯宣布公司架构调整,在原有七个事业群的基础上进行重组整合,形成新的6个事业群,在本次调整中,腾讯新成立了云与智慧产业事业群(CSIG&…

Android实现边缘凹凸的View

转载 最近做项目的时候遇到一个卡劵的效果,由于自己觉得用图片来做的话可以会出现适配效果不好,再加上自己自定义view方面的知识比较薄弱,所以想试试用自定义View来实现。但是由于自己知识点薄弱,一开始居然想着用画矩形来设置边…

【报告解读】126个国家、29个行业、36位高管认为AI的未来这么走

来源:网易智能人工智能(AI)已经使早期采用它的制造商能够更好地协调分析、商业智能(BI)、移动性和实时监控,以实现更快的营收增长,并比同行更快地成长壮大。如今,最顶级的18%的AI采用者将超过70%的精力投入…