A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement
for either the width or the height. A MeasureSpec is comprised of a size and a mode. There are three possible modes:
- UNSPECIFIED
- The parent has not imposed any constraint on the child. It can be whatever size it wants.
- EXACTLY
- The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
- AT_MOST
- The child can be as large as it wants up to the specified size.
1.使用 View.resolveSize(int size,int measureSpec)
public static int resolveSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
result = Math.min(size, specSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
2.源码
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT; public static int makeMeasureSpec(int size, int mode) {
return size + mode;
} public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
} public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
} }
示例:
int measureSpec = makeMeasureSpec(4, EXACTLY);
getSize(measureSpec);
getMode(measureSpec);
-----------------------makeMeasureSpec --------------------- mode+size
mode: 1000 0000 0000 0000 0000 0000 0000 000
size: 100
measureSpec: 1000 0000 0000 0000 0000 0000 0000 100
---------------getMode ---------------- MODE_MASK & measureSpec
MODE_MASK: 1100 0000 0000 0000 0000 0000 0000 0000
measureSpec: 1000 0000 0000 0000 0000 0000 0000 100
3.示例ListView.measureItem(View child)
private void measureItem(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
} int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
4.重写ListView与ScrollView 兼容:
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
Parameters
- widthMeasureSpec horizontal space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.
- heightMeasureSpec vertical space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.
重写:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec); }
ListView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec); int childWidth = 0;
int childHeight = 0; mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||
heightMode == MeasureSpec.UNSPECIFIED)) {
final View child = obtainView(0); measureScrapChild(child, 0, widthMeasureSpec); childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight(); if (recycleOnMeasure()) {
mRecycler.addScrapView(child);
}
} if (widthMode == MeasureSpec.UNSPECIFIED) {
widthSize = mListPadding.left + mListPadding.right + childWidth +
getVerticalScrollbarWidth();
} if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = mListPadding.top + mListPadding.bottom + childHeight +
getVerticalFadingEdgeLength() * 2;
} if (heightMode == MeasureSpec.AT_MOST) {
// TODO: after first layout we should maybe start at the first visible position, not 0
heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
} setMeasuredDimension(widthSize, heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}