您的位置:首页 > 移动开发 > Android开发

Android源码分析之---View.MeasureSpec 解析

2014-04-01 16:53 393 查看

1、简介

    一个MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。它有三种模式:

UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;

EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;

AT_MOST(至多),子元素至多达到指定大小的值。

     (ps:上段转自http://blog.csdn.net/kaixinbingju/article/details/8649218

MeasureSpecs的实现减少了对象的分配,这个类提供了 <size, mode>的pack和unpack进入这个int。

2、源码分析

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();
}
}

3、Demo进行分析MeasureSpec中提供的方法。

这个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);
便可以获得对应的值。

运行时,产生的日志



4 、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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: