一、MeasureSpec简介
MeasureSpec是Android View系统中用于描述View在MeasureSpec上的要求,定义为一个32位的int类型变量。
32位的MeasureSpec中的高2位表示MeasureSpec的模式(Mode),剩下的30位表示MeasureSpec的大小(Size)。
二、MeasureSpec的三种模式
MeasureSpec的三种模式分别是UNSPECIFIED、EXACTLY和AT_MOST。
1、UNSPECIFIED模式(即为0)表示此View对尺寸没有任何参考意义,父View可以按照子View实际尺寸分配给它任意的空间。对于UNSPECIFIED模式的子View,虽然可以设置任意的宽高,但是其实意义并不是很大,对大部分View没有意义。
2、EXACTLY模式(即为MeasureSpec.EXACTLY,值为1073741824)表示View的确切大小已知,此时MeasureSpec中的size表示View的大小。对于EXACTLY模式的子View,想要占据特定的大小,需要确切的设定尺寸,否则将会导致View的不确定位置。
3、AT_MOST模式(即为MeasureSpec.AT_MOST,值为-2147483648)表示此View的尺寸是最大可获得的尺寸。对于AT_MOST模式的View,应该尽量适应于制约因素,尽量使得其不大于Specified Size的尺寸,否则就是不确定的,会影响其他控件的布局。
三、MeasureSpec的计算规则
MeasureSpec是通过父View的MeasureSpec和子View的LayoutParams来计算得到的。MeasureSpec的计算规则包括以下步骤:
1、获取父View的MeasureSpec以及父View的padding值(因为padding的值也是父View尺寸的一部分,所以需要将padding的值算入到父View的尺寸中)。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); // 去除padding后的父View宽高 int parentWidth = widthSize - paddingLeft - paddingRight; int parentHeight = heightSize - paddingTop - paddingBottom;}
2、通过MeasureSpec.makeMeasureSpec方法,将父View的MeasureSpec和LayoutParams中的尺寸相结合,来计算子View的MeasureSpec。
public static int makeMeasureSpec(int size, int mode) { if (mode == UNSPECIFIED) { return size; } else { return size + mode; }}protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); // 去除padding后的父View宽高 int parentWidth = widthSize - paddingLeft - paddingRight; int parentHeight = heightSize - paddingTop - paddingBottom; // 通过makeMeasureSpec方法,计算子View的宽度和高度MeasureSpec int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.AT_MOST);}
3、通过measureChildWithMargins或measureChildren方法,测量出子View实际的宽度和高度。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); // 去除padding后的父View宽高 int parentWidth = widthSize - paddingLeft - paddingRight; int parentHeight = heightSize - paddingTop - paddingBottom; // 通过makeMeasureSpec方法,计算子View的宽度和高度MeasureSpec int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(200, MeasureSpec.AT_MOST); // 测量子View的宽高 measureChildren(childWidthMeasureSpec, childHeightMeasureSpec);}
四、MeasureSpec的常见使用场景
MeasureSpec的常见使用场景包括:TableView的单元格高度、ListView/RecyclerView的Item高度、自定义View的测量等。
1、TableView的单元格高度
public void setCellWidthAndHeight(int width, int height) { LayoutParams lp = new LayoutParams(width, height); if (mTableLayout != null) { TableRow tableRow = mTableLayout.findViewById(ROW_ID + mRowIndex); View childView = tableRow.findViewById(COLUMN_ID + mColumnIndex); if (childView != null) { int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); childView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } }}
2、ListView/RecyclerView的Item高度
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null); int parentWidth = parent.getMeasuredWidth(); int itemHeight = parentWidth / 2; int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY); itemView.measure(childWidthMeasureSpec, childHeightMeasureSpec); return new MyViewHolder(itemView);}
3、自定义View的测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); // 去除padding后的父View宽高 int parentWidth = widthSize - paddingLeft - paddingRight; int parentHeight = heightSize - paddingTop - paddingBottom; // 计算子View的宽高MeasureSpec int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, LayoutParams.WRAP_CONTENT); int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, LayoutParams.WRAP_CONTENT); // 测量子View的宽高 measureChildren(childWidthMeasureSpec, childHeightMeasureSpec);}
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/248930.html