一个MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。它有三种模式:
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
(ps:上段转自http://blog.csdn.net/kaixinbingju/article/details/8649218 )
MeasureSpecs的实现减少了对象的分配,这个类提供了 <size, mode>的pack和unpack进入这个int。
MeasureSpec 源码
public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /** * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /** * Measure specification mode: 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. */ public static final int EXACTLY = 1 << MODE_SHIFT; /** * Measure specification mode: The child can be as large as it wants up * to the specified size. */ public static final int AT_MOST = 2 << MODE_SHIFT; /** * Creates a measure specification based on the supplied size and mode. * 将尺寸和mode pack到 提供的 a measure specifiaction 中。 * The mode must always be one of the following: * <ul> * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> * </ul> * * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's * implementation was such that the order of arguments did not matter * and overflow in either value could impact the resulting MeasureSpec. * {@link android.widget.RelativeLayout} was affected by this bug. * Apps targeting API levels greater than 17 will get the fixed, more strict * behavior.</p> * * @param size the size of the measure specification * @param mode the mode of the measure specification * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } /** * Extracts the mode from the supplied measure specification. * 获得 mode(前面提到三个模式之一) * @param measureSpec the measure specification to extract the mode from * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, * {@link android.view.View.MeasureSpec#AT_MOST} or * {@link android.view.View.MeasureSpec#EXACTLY} */ public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } /** * Extracts the size from the supplied measure specification. * 获取测量的具体的值 * @param measureSpec the measure specification to extract the size from * @return the size in pixels defined in the supplied measure specification */ public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } static int adjust(int measureSpec, int delta) { return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); } /** * Returns a String representation of the specified measure * specification. * * @param measureSpec the measure specification to convert to a String * @return a String with the following format: "MeasureSpec: MODE SIZE" */ public static String toString(int measureSpec) { int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb = new StringBuilder("MeasureSpec: "); if (mode == UNSPECIFIED) sb.append("UNSPECIFIED "); else if (mode == EXACTLY) sb.append("EXACTLY "); else if (mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } }
这个Demo修改自提供APIdemo中的LabelView。
public class LabelView extends View { private Paint mTextPaint; private String mText; private int mAscent; /** * Constructor. This version is only needed if you will be instantiating * the object manually (not from a layout XML file). * @param context */ public LabelView(Context context) { super(context); initLabelView(); } /** * Construct object, initializing with any attributes we understand from a * layout file. These attributes are defined in * SDK/assets/res/any/classes.xml. * * @see android.view.View#View(android.content.Context, android.util.AttributeSet) */ public LabelView(Context context, AttributeSet attrs) { super(context, attrs); initLabelView(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabelView); CharSequence s = a.getString(R.styleable.LabelView_text); if (s != null) { setText(s.toString()); } // Retrieve the color(s) to be used for this view and apply them. // Note, if you only care about supporting a single color, that you // can instead call a.getColor() and pass that to setTextColor(). setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000)); int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0); if (textSize > 0) { setTextSize(textSize); } a.recycle(); } private final void initLabelView() { mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); // Must manually scale the desired text size to match screen density mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density); mTextPaint.setColor(0xFF000000); setPadding(3, 3, 3, 3); } /** * Sets the text to display in this label * @param text The text to display. This will be drawn as one line. */ public void setText(String text) { mText = text; requestLayout(); invalidate(); } /** * Sets the text size for this label * @param size Font size */ public void setTextSize(int size) { // This text size has been pre-scaled by the getDimensionPixelOffset method mTextPaint.setTextSize(size); requestLayout(); invalidate(); } /** * Sets the text color for this label. * @param color ARGB value for the text */ public void setTextColor(int color) { mTextPaint.setColor(color); invalidate(); } @Override protected void onFinishInflate() { // TODO Auto-generated method stub super.onFinishInflate(); System.out.println("onFinishInflate"); } /** * @see android.view.View#measure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { System.out.println("onMeasure "+widthMeasureSpec+" "+heightMeasureSpec); System.out.println("onMeasure "+Integer.toBinaryString(widthMeasureSpec)+" "+Integer.toBinaryString(heightMeasureSpec)); //设置这个view的尺寸 setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * * 测量决定这个View的width * @param measureSpec A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureWidth(int measureSpec) { int result = 0; // 通过这个 a measure specifiaction 值获取mode int specMode = MeasureSpec.getMode(measureSpec); // 通过这个 a measure specifiaction 值获取view的具体的尺寸 int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text result = (int) mTextPaint.measureText(mText) + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } System.out.println("pack height:"+MeasureSpec.makeMeasureSpec(result, specMode)); System.out.println("measureWidth "+result); return result; } /** * 测量决定这个View的height * @param measureSpec A measureSpec packed into an int * @return The height of the view, honoring constraints from measureSpec */ private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); mAscent = (int) mTextPaint.ascent(); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text (beware: ascent is a negative number) result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } System.out.println("measureHeight "+result); return result; } /** * Render the text * * @see android.view.View#onDraw(android.graphics.Canvas) */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); System.out.println("onDraw"); canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint); } }我们可以看到通过MeasureSpec 中
MeasureSpec.makeMeasureSpec(result, specMode) pack了view的size和mode(这样的优势是 减少了对象的创建,提高了性能。)
在需要的size和mode的时候,通过
int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);便可以获得对应的值。
运行时,产生的日志
在这个Demo中,我们结合上面的计算的结果来分析,其如何对size进行pack和unpack计算的。
public class MeasureSpec { public static void main(String[] args) { int widthMeasureSpec=1073742256 ; // 通过这个 a measure specifiaction 值获取mode int specMode = MeasureSpec.getMode(widthMeasureSpec); // 通过这个 a measure specifiaction 值获取view的具体的尺寸 int specSize = MeasureSpec.getSize(widthMeasureSpec); int widthMeasureSpec2= MeasureSpec.makeMeasureSpec(specSize, specMode); System.out.println(specMode); System.out.println(specSize); System.out.println("\n"+Integer.toBinaryString(specMode)); System.out.println(Integer.toBinaryString(EXACTLY)); System.out.println("\n"+widthMeasureSpec2); } 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); } static int adjust(int measureSpec, int delta) { return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec)); } public static String toString(int measureSpec) { int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb = new StringBuilder("MeasureSpec: "); if (mode == UNSPECIFIED) sb.append("UNSPECIFIED "); else if (mode == EXACTLY) sb.append("EXACTLY "); else if (mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } }
此处的 移位、位与、或、异或、非。 可参考 Java 位运算(移位、位与、或、异或、非)
http://my.oschina.net/banxi/blog/51247
http://blog.csdn.net/kaixinbingju/article/details/8649218