在自定义控件时为了满足特定需求,widget大都是我们自己测量的。
大家都知道测量时最重要的步骤就是重写onMeasure方法,来计算出宽高。
这里面的MeasureSpec 很重要,大家也都知道,它是一个java中的静态类,它有重要的三个静态常量和三个最重要的静态方法。
我这里说下MeasureSpec 的3中模式
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
看过很多博客,大家总结说,模式和XML布局有如下对应关系:
wrap_content-> MeasureSpec.AT_MOST
match_parent -> MeasureSpec.EXACTLY
50dp(确切值) -> MeasureSpec.EXACTLY
但是在具体开发中 我发现并不是一定这样,然后就看了下源码,才发现子控件的mode不只是与自身的设置有关系,它还受到父容器影响。
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 us// 父容器是 EXACTLY 模式case MeasureSpec.EXACTLY:if (childDimension >= 0) {// 如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLYresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.// child 希望尺寸与 父容器 一样大,那么允许它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.// child 希望由自己决定自己的尺寸,但是有个前提是它不能大于 父容器 本身给的建议值。// 并且需要设置它的测量模式为 AT_MOSTresultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on us// 父容器是 AT_MOST 模式,对于 父容器 本身而言,它也有个最大的尺寸约束case MeasureSpec.AT_MOST:if (childDimension >= 0) {// Child wants a specific size... so be it// 如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLYresultSize = 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.// child 希望尺寸和 父容器 一样大,那么允许它,但是 父容器 本身有限制,所以也// 需要给 child 加一个限制,这个限制就是将 child 模式设置为 AT_MOSTresultSize = 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.// child 希望由自己决定自己的尺寸,但是有个前提是它不能大于 父容器 本身给的建议值。// 并且需要设置它的测量模式为 AT_MOSTresultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to be// 父容器是 UNSPECIFIED 模式,对于 父容器 本身而言,它的期望值是想要多大就多大,让 父容器 的 parent不要限制它。case MeasureSpec.UNSPECIFIED:if (childDimension >= 0) {// Child wants a specific size... let him have it//如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLYresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should// be// child 希望尺寸和 父容器 一样大,那么允许它,但是 父容器 本身也需要计算,所以只能设置为0,并且// 将child的测量模式设置为 UNSPECIFIEDresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how// big it should be// child 希望尺寸由自己决定,一般这个时候,父容器 会给它一个 Size 作为最大值限制,// 但是 父容器 本身也需要计算,所以只能设置为0,并且没有给child最大值的设定// 将child的测量模式设置为 UNSPECIFIEDresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;}break;}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
总结来说 mode 取值 受父容器与子控件共同决定
父容器是MeasureSpec.EXACTLY模式
子控件是具体值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.EXACTLY
子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST
父容器是MeasureSpec.AT_MOST模式
子控件是具体值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.AT_MOST
子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST
父容器是MeasureSpec.UNSPECIFIED模式
子控件是具体值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.UNSPECIFIED
子控件是WRAP_CONTENT, mode = MeasureSpec.UNSPECIFIED
就这样