在 onMeasure中的参数 heightMeasureSpec 又是什么?
MeasureSpec是父控件提供给子View的onMeaure参数,作为设定自身大小参考,只是个参考,要多大,还是View自己说了算。
MeasureSpec 是一个复合参数,包含了specMode和specSize,可以通过如下获取。
1 2
| int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);
|
也可以通过如下合并
1
| MeasureSpec.makeMeasureSpec(resultSize, resultMode);
|
specMode 有3种模式
- UNSPECIFIED:不对View大小做限制,如:ListView,ScrollView
- EXACTLY:确切的大小,如:100dp或者march_parent
- AT_MOST:大小不可超过某数值,如:wrap_content
子View的MeasureSpec由父View的MeasureSpec和子View本身的LayoutPramas共同决定,在ViewGroup的getChildMeasureSpec方法中实现,具体解析在下面代码中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
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) { case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
case MeasureSpec.AT_MOST: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;
case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; }
return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
|
- MeasureSpec不管父容器是什么szie和mode,如果child view 指定了高度,都采用自己的高度,mode也是EXACTLY
- 当子View接收到父控件传递的MeasureSpec的时候,就可以知道父控件希望自己如何显示,这个点对于开发者而言就是onMeasure函数
- ViewGroup在计算自己尺寸的时候,必须预先知道所有子View的尺寸
- 如果父控件传递给的MeasureSpec的mode是MeasureSpec.UNSPECIFIED,就说明,父控件对自己没有任何限制,那么尺寸就选择自己需要的尺寸size
- 如果父控件传递给的MeasureSpec的mode是MeasureSpec.EXACTLY,就说明父控件有明确的要求,希望自己能用measureSpec中的尺寸,这时就推荐使用MeasureSpec.getSize(measureSpec)
- 如果父控件传递给的MeasureSpec的mode是MeasureSpec.AT_MOST,就说明父控件希望自己不要超出MeasureSpec.getSize(measureSpec),如果超出了,就选择MeasureSpec.getSize(measureSpec),否则用自己想要的尺寸就行了
ViewGroup的尺寸其实只需要三部:
- 测量所有子View,获取所有子View的尺寸
- 根据自身特点计算所需要的尺寸
- 综合考量需要的尺寸跟父控件传递的MeasureSpec,得出一个合理的尺寸
顶层View的MeasureSpec是谁指定
传递给子View的MeasureSpec是父容器根据自己的MeasureSpec及子View的布局参数所确定的,那么根MeasureSpec是谁创建的呢?我们用最常用的两种Window来解释一下,Activity与Dialog,DecorView是Activity的根布局,传递给DecorView的MeasureSpec是系统根据Activity或者Dialog的Theme来确定的,也就是说,最初的MeasureSpec是直接根据Window的属性构建的,一般对于Activity来说,根MeasureSpec是EXACTLY+屏幕尺寸,对于Dialog来说,如果不做特殊设定会采用AT_MOST+屏幕尺寸。这里牵扯到WindowManagerService跟ActivityManagerService
感谢
1 2 3 4 5 6
| 链接:https://www.jianshu.com/p/cecd0de7ec27 来源:简书
作者:看书的小蜗牛 链接:https://www.jianshu.com/p/d16ec64181f2 来源:简书
|