一、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/zh-tw/n/248930.html