一、android中view的measure过程总概
   视图大小计算的过程是从根视图measure()方法开始,接着该方法会调用根视图的onMeasure()方法,onMeasure()方法会对所包含的子视图逐一执行measure()方法,如果子视图是ViewGroup子类对象(LinearLayout、FrameLayout、RelativeLayout等布局),则继续调用子视图的measure()方法,重复这一过程。如果子视图是View子类对象(Button、EditText、TextView、ImageView等),则在子视图重载的onMeasure方法内部不需要进行对子视图进行measure操作,从而一次measure过程完成。过程如下图所示:

二、measure详细过程

View中的measure()方法源码(ViewGroup类继承了View类,measure过程先从ViewGroup子类开始):

[java] view plaincopy
  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  2. if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
  3. widthMeasureSpec != mOldWidthMeasureSpec ||
  4. heightMeasureSpec != mOldHeightMeasureSpec) {
  5. // first clears the measured dimension flag
  6. mPrivateFlags &= ~MEASURED_DIMENSION_SET;
  7. if (ViewDebug.TRACE_HIERARCHY) {
  8. ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
  9. }
  10. // measure ourselves, this should set the measured dimension flag back
  11. onMeasure(widthMeasureSpec, heightMeasureSpec);
  12. // flag not set, setMeasuredDimension() was not invoked, we raise
  13. // an exception to warn the developer
  14. if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
  15. throw new IllegalStateException("onMeasure() did not set the"
  16. + " measured dimension by calling"
  17. + " setMeasuredDimension()");
  18. }
  19. mPrivateFlags |= LAYOUT_REQUIRED;
  20. }
  21. mOldWidthMeasureSpec = widthMeasureSpec;
  22. mOldHeightMeasureSpec = heightMeasureSpec;
  23. }
   注:通过源码,我们看到该方法的定义中用了final关键字,说明该方法是不能被重写的,即View系统定义的这个measure框架不能被修改。参数widthMeasureSpec和heightMeasureSpec分别对应宽和高的measureSpec,当父视图对子视图进行measure操作时,会调用子视图的measure()方法,该参数得意思是父视图所提供的measure的“规格”,因为父视图为子视图提供的窗口尺寸是由父视图和子视图共同决定。该参数有两部分组成,第一部分:高16位表示specMode,定义在MeasureSpec类中,有三种类型:MeasureSpec.EXACTLY:表示明确的尺寸大小, MeasureSpec.AT_MOST:表示最大大小, MeasureSpec.UNSPECIFIED:不确定大小。第二部分:低16位表示size,即父view的大小,这就是为什么我们在重写onmeasure方法是需要:int specMode = MeasureSpec.getMod(spec); int specSize = MeasureSpec.getSize(spec)这样调用。specMode一般都为MeasureSpec.EXACTLY ,而size分别对应屏幕宽,高。也就是Window第一次掉用的view,一般都是这个值,而对于子view来说,这个值就是你在xml定义的属性  android:layout_width和android:layout_height的值。
   下面我们看看源码执行过程,看注释就能很明白,首先清 除测量尺寸的标识。接着将重新测量自己的尺寸,即调用onMeasure()方法。最后是判断测量尺寸大小的标识是否已经重新赋值,如果没有则不执行setMeasuredDimension()方法。方法结束。这个方法里面主要就是调用自己的onMeasure()方法,对自己的大小尺寸进行测量。下面来介绍onMeasure()方法。

ViewGroup中的onMeasure方法介绍

其实在ViewGroup类中并没有重写该方法,一般在他的子类中进行重写,比如LinearLayout、RelativeLayout,下面我们以Linearlayout来分析。LinearLayout中onMeasure方法源码如下:
[java] view plaincopy
  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. if (mOrientation == VERTICAL) {
  4. measureVertical(widthMeasureSpec, heightMeasureSpec);
  5. } else {
  6. measureHorizontal(widthMeasureSpec, heightMeasureSpec);
  7. }
  8. }

注:通过源码我们可以知道,首先onMeasure会判断这个布局是纵向布局还是横向布局,即对应android:orientation=""属性。下面以纵向布局来分析,源码如下,有点长:

[java] view plaincopy
  1. /**
  2. * Measures the children when the orientation of this LinearLayout is set
  3. * to {@link #VERTICAL}.
  4. *
  5. * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
  6. * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
  7. *
  8. * @see #getOrientation()
  9. * @see #setOrientation(int)
  10. * @see #onMeasure(int, int)
  11. */
  12. void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
  13. mTotalLength = 0;
  14. int maxWidth = 0;
  15. int alternativeMaxWidth = 0;
  16. int weightedMaxWidth = 0;
  17. boolean allFillParent = true;
  18. float totalWeight = 0;
  19. final int count = getVirtualChildCount();
  20. final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  21. final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  22. boolean matchWidth = false;
  23. final int baselineChildIndex = mBaselineAlignedChildIndex;
  24. final boolean useLargestChild = mUseLargestChild;
  25. int largestChildHeight = Integer.MIN_VALUE;
  26. // See how tall everyone is. Also remember max width.
  27. for (int i = 0; i < count; ++i) {
  28. final View child = getVirtualChildAt(i);
  29. if (child == null) {
  30. mTotalLength += measureNullChild(i);
  31. continue;
  32. }
  33. if (child.getVisibility() == View.GONE) {
  34. i += getChildrenSkipCount(child, i);
  35. continue;
  36. }
  37. LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
  38. totalWeight += lp.weight;
  39. if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
  40. // Optimization: don't bother measuring children who are going to use
  41. // leftover space. These views will get measured again down below if
  42. // there is any leftover space.
  43. final int totalLength = mTotalLength;
  44. mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
  45. } else {
  46. int oldHeight = Integer.MIN_VALUE;
  47. if (lp.height == 0 && lp.weight > 0) {
  48. // heightMode is either UNSPECIFIED or AT_MOST, and this
  49. // child wanted to stretch to fill available space.
  50. // Translate that to WRAP_CONTENT so that it does not end up
  51. // with a height of 0
  52. oldHeight = 0;
  53. lp.height = LayoutParams.WRAP_CONTENT;
  54. }
  55. // Determine how big this child would like to be. If this or
  56. // previous children have given a weight, then we allow it to
  57. // use all available space (and we will shrink things later
  58. // if needed).
  59. measureChildBeforeLayout(
  60. child, i, widthMeasureSpec, 0, heightMeasureSpec,
  61. totalWeight == 0 ? mTotalLength : 0);
  62. if (oldHeight != Integer.MIN_VALUE) {
  63. lp.height = oldHeight;
  64. }
  65. final int childHeight = child.getMeasuredHeight();
  66. final int totalLength = mTotalLength;
  67. mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
  68. lp.bottomMargin + getNextLocationOffset(child));
  69. if (useLargestChild) {
  70. largestChildHeight = Math.max(childHeight, largestChildHeight);
  71. }
  72. }
  73. /**
  74. * If applicable, compute the additional offset to the child's baseline
  75. * we'll need later when asked {@link #getBaseline}.
  76. */
  77. if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
  78. mBaselineChildTop = mTotalLength;
  79. }
  80. // if we are trying to use a child index for our baseline, the above
  81. // book keeping only works if there are no children above it with
  82. // weight.  fail fast to aid the developer.
  83. if (i < baselineChildIndex && lp.weight > 0) {
  84. throw new RuntimeException("A child of LinearLayout with index "
  85. + "less than mBaselineAlignedChildIndex has weight > 0, which "
  86. + "won't work.  Either remove the weight, or don't set "
  87. + "mBaselineAlignedChildIndex.");
  88. }
  89. boolean matchWidthLocally = false;
  90. if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
  91. // The width of the linear layout will scale, and at least one
  92. // child said it wanted to match our width. Set a flag
  93. // indicating that we need to remeasure at least that view when
  94. // we know our width.
  95. matchWidth = true;
  96. matchWidthLocally = true;
  97. }
  98. final int margin = lp.leftMargin + lp.rightMargin;
  99. final int measuredWidth = child.getMeasuredWidth() + margin;
  100. maxWidth = Math.max(maxWidth, measuredWidth);
  101. allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
  102. if (lp.weight > 0) {
  103. /*
  104. * Widths of weighted Views are bogus if we end up
  105. * remeasuring, so keep them separate.
  106. */
  107. weightedMaxWidth = Math.max(weightedMaxWidth,
  108. matchWidthLocally ? margin : measuredWidth);
  109. } else {
  110. alternativeMaxWidth = Math.max(alternativeMaxWidth,
  111. matchWidthLocally ? margin : measuredWidth);
  112. }
  113. i += getChildrenSkipCount(child, i);
  114. }
  115. if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
  116. mTotalLength = 0;
  117. for (int i = 0; i < count; ++i) {
  118. final View child = getVirtualChildAt(i);
  119. if (child == null) {
  120. mTotalLength += measureNullChild(i);
  121. continue;
  122. }
  123. if (child.getVisibility() == GONE) {
  124. i += getChildrenSkipCount(child, i);
  125. continue;
  126. }
  127. final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
  128. child.getLayoutParams();
  129. // Account for negative margins
  130. final int totalLength = mTotalLength;
  131. mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
  132. lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
  133. }
  134. }
  135. // Add in our padding
  136. mTotalLength += mPaddingTop + mPaddingBottom;
  137. int heightSize = mTotalLength;
  138. // Check against our minimum height
  139. heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
  140. // Reconcile our calculated size with the heightMeasureSpec
  141. heightSize = resolveSize(heightSize, heightMeasureSpec);
  142. // Either expand children with weight to take up available space or
  143. // shrink them if they extend beyond our current bounds
  144. int delta = heightSize - mTotalLength;
  145. if (delta != 0 && totalWeight > 0.0f) {
  146. float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
  147. mTotalLength = 0;
  148. for (int i = 0; i < count; ++i) {
  149. final View child = getVirtualChildAt(i);
  150. if (child.getVisibility() == View.GONE) {
  151. continue;
  152. }
  153. LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
  154. float childExtra = lp.weight;
  155. if (childExtra > 0) {
  156. // Child said it could absorb extra space -- give him his share
  157. int share = (int) (childExtra * delta / weightSum);
  158. weightSum -= childExtra;
  159. delta -= share;
  160. final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
  161. mPaddingLeft + mPaddingRight +
  162. lp.leftMargin + lp.rightMargin, lp.width);
  163. // TODO: Use a field like lp.isMeasured to figure out if this
  164. // child has been previously measured
  165. if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
  166. // child was measured once already above...
  167. // base new measurement on stored values
  168. int childHeight = child.getMeasuredHeight() + share;
  169. if (childHeight < 0) {
  170. childHeight = 0;
  171. }
  172. child.measure(childWidthMeasureSpec,
  173. MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
  174. } else {
  175. // child was skipped in the loop above.
  176. // Measure for this first time here
  177. child.measure(childWidthMeasureSpec,
  178. MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
  179. MeasureSpec.EXACTLY));
  180. }
  181. }
  182. final int margin =  lp.leftMargin + lp.rightMargin;
  183. final int measuredWidth = child.getMeasuredWidth() + margin;
  184. maxWidth = Math.max(maxWidth, measuredWidth);
  185. boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
  186. lp.width == LayoutParams.MATCH_PARENT;
  187. alternativeMaxWidth = Math.max(alternativeMaxWidth,
  188. matchWidthLocally ? margin : measuredWidth);
  189. allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
  190. final int totalLength = mTotalLength;
  191. mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
  192. lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
  193. }
  194. // Add in our padding
  195. mTotalLength += mPaddingTop + mPaddingBottom;
  196. // TODO: Should we recompute the heightSpec based on the new total length?
  197. } else {
  198. alternativeMaxWidth = Math.max(alternativeMaxWidth,
  199. weightedMaxWidth);
  200. }
  201. if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
  202. maxWidth = alternativeMaxWidth;
  203. }
  204. maxWidth += mPaddingLeft + mPaddingRight;
  205. // Check against our minimum width
  206. maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
  207. setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);
  208. if (matchWidth) {
  209. forceUniformWidth(count, heightMeasureSpec);
  210. }
  211. }
注:
  1. 获取所有的子view数量,对每个子view开始处理,如果子view是GONE的,则直接跳过。
  2. 获取子view的LayoutParams,在xml中定义的参数,通过layout_weight定义的值累加到变量totalWeight中,然后判断如果view的height设置为零,但weight设置的大于0,则将height的值设置为LayoutParams.WRAP_CONTENT。
  3. 然后调用measureChildWithMargins方法,该方法处理的逻辑:计算子view的measureSpec,即specMode和specSize,调用方法为:getChildMeasureSpec,调用两次,分别 计算宽和高,getChildMeasureSpec内部根据父view的measure和子view的layout_width和layout_height属性计算子view的measure。getChildMeasureSpec计算子view的measure,总结如下:1.如果在xml中指定了子view的具体大小,那么计算结果不管父的measure是什么,结果都是EXACITY+child_size,2.如果子view的height指定的值为FILL_PARENT,则返回的结果为:EXACITY+size,原因很简单:因为FILL_PARENT的意思是充满这个父view,所以返回的子view的measure就是view的大小。3.如果子view的大小为wrap_content,那么返回的结果都为AT_MOST+size,原因是:最大不能超过父view的大小。
  4. 子view的measure确定好以后,然后调用子view的measure方法,如果子view是View对象,则该view的大小测量结束,开始下一个子view的循环,如果子view是ViewGroup那么,又开始一个新的递归,处理逻辑和上面一样,直到所有的view对象测量结束。
  5. 所有的子view测量结束后,才开始对layout_weight计算,这样我们可能想到,如果父view已经被占满了,那么有可能layout_weight大于0的view对象是不会显示的,而计算layout_weight的方法也很简单,就是用总高度减去上面分析完mTotalLength的值,就是剩下,然后去平分给view对象,注意计算权重时优先去android:android:weightSum(LinearLayout的xml属性)的值,如果不设置该值会计算和,所以该值既然设置了,就一定要子view的weight的总和相等,否则平分可能不能得到预期效果。

过程分析完毕,这篇文章这里提到了LinearLayout中的layout_weight属性,这个属性对很对人来说是又恨又爱,下篇文章,我们将来总结改属性的详细用法,让大家彻底理解这个属性。

View工作原理(三)视图大小计算过程(measure过程)相关推荐

  1. 操作系统之内存管理:1、内存管理基础知识(指令工作原理、地址转化、程序运行过程)

    1.内存管理基础知识(指令工作原理.地址转化.程序运行过程) 思维导图 什么是内存? 指令的工作原理 装入模块的三种实现 绝对装入 可重定位装入 动态重定位 程序的运行过程 链接的三种方式 思维导图 ...

  2. view工作原理-计算视图大小的过程(onMeasure)

    view的视图有两种情况: 内容型视图:由视图的内容决定其大小. 图形型视图:父视图为view动态调整大小. ### measure的本质 把视图布局使用的"相对值"转化成具体值的 ...

  3. View工作原理(一)事件传递原理详解

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17353869 ...

  4. 详解滤波电路工作原理及相关参数计算

    在整流电路输出的电压是单向脉动性电压,不能直接给电子电路使用.所以要对输出的电压进行滤波, 消除电压中的交流成分,成为直流电后给电子电路使用.在滤波电路中,主要使用对交流电有特殊阻抗特性的器件,如:电 ...

  5. Struts2→拦截器、工作原理、拦截器计算Action执行时间、Struts2自带拦截器、拦截器栈

    工作原理 实现拦截器 样例 Struts2自带拦截器 拦截器栈(先进后出) TOKEN防表单重复提交 文字拦截器 AJAX

  6. View工作原理(四)view的layout过程

    刚过完自己的本命年,新的一年希望自己有个新的开始,祝自己在新的一年里一帆风顺,同时也祝广大的朋友们新年新气象,收获多多! 一.android中view的layout过程总概 Layout过程其实就是父 ...

  7. View工作原理(二)导致View重建原因

    导致View重建的原因有三个: 1.视图本身内部状态变化引起重绘: 2.view树内部添加或者删除子view: 3.View本身的大小及可见性发生变化. 这三种情况最后都直接或者间接的调用到了三个方法 ...

  8. 定时器工作原理及初值快速计算

    2019独角兽企业重金招聘Python工程师标准>>> 确定定时器的初值是为了让定时器按照自己的意思(自己规定的秒数)去产生中断.由于定时器计数是用两个8位寄存器结合为一个16位去递 ...

  9. 路由器下一跳地址怎么判断_CCNP-路由器工作原理

    好物要写,我们的文章也不能断更,赚钱重要,学习新知识更重要,今天就是进入NP阶段的内容了,我们会深入理解每个动态路由协议的工作原理,以及一些策略方面的配置,至此你就能随心所欲控制路由的走向,让他走你所 ...

最新文章

  1. ubuntu 安装docker_Docker: 教程04 - (初始化安装之在 Ubuntu 安装Docker CE)
  2. listen()与accept()函数的区别
  3. MFC下绘制曲线工具Teechart使用
  4. ant接口自动化 junit_ant 学习(3)--结合junit形成自动化测试小框架
  5. jQuery的ajax()、post()方法提交数组,参数[] 问题
  6. 先判断对象是否为NULL再进行操作
  7. file.delete删除不了文件_非常实用的文件服务系统
  8. android 屏幕方向监听,android 屏幕旋转问题 - jwzhangjie的个人空间 - 51Testing软件测试网 51Testing软件测试网-软件测试人的精神家园...
  9. java中web错误返回码,Java-Web3j Transfer.sendFunds()返回错误“天然气...
  10. sqlserver中where条件加判断
  11. Python requests抓取有道翻译 最新版破解js加密
  12. 12个C语言必背实例
  13. moments音标_moments是什么意思_moments的翻译_音标_读音_用法_例句_爱词霸在线词典...
  14. python 残差图_利用pyFOAM残差的输出
  15. java web景点规划导航
  16. 机电毕业设计----利用CC2530芯片开发的基于ZigBee技术的灌溉模拟系统----LED显示屏代码解释(源代码)
  17. ZJOI2017 仙人掌
  18. Linux服务器常用命令 - 记录(Anaconda/Matlab/VNC/Python)
  19. 解决VS CODE官网下载速度慢的问题
  20. Amazing grace 奇异恩典

热门文章

  1. 【C 语言】文件操作 ( 配置文件读写 | 完整代码示例 ) ★
  2. 【C 语言】数组作为参数退化为指针问题 ( 问题描述 | 从编译器角度分析该问题 | 出于提高 C 语言执行效率角度考虑 | 数组作为参数的推荐方案 )
  3. 【Android 逆向】frida 框架安装 ( 设置 Python 3.7 版本 | 安装 frida 12.7.5 版本 | 安装 frida-tools 5.1.0 版本 )
  4. 【Kotlin】Kotlin 类的继承 三 ( super 关键字使用 | super@ 外部调用父类方法 | 子类选择性调用 父类 / 接口 方法 super )
  5. 3创建型模式之单例模式
  6. C#单例模式的懒汉与饿汉
  7. react 的props和state
  8. map/set/object/array对比
  9. Centos 安装Mongo DB
  10. 关于ionic的跨域问题