measure 调用onMeasure(),因为measure的到的宽高会用MeasureSpec封装,先看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;
MeasureSpec包含三种模式(mode)
UNSPECIFIED:父控件未约束,也就是子控件大小随意,比如listview,scrollview一般view用不到
EXACTLY:当宽或高为固定值或match_parent时,子控件约束值是固定的,mode为exactly
AT_MOST:当宽或高为wrap_content时,我觉得当viewgroup有两个控件时 一个width 60dp,一个width 70dp,那么
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
知道1:mode_mask 为11000.... 所以~mode_mask 为001111...
2:一个二进制数&1的结果不变,一个二进制数&0的结果都是0
3:一个二进制数 | 1都得1,一个二进制 | 0不变。
110000 | 001100 = 111100 可见 如果将一个二进制数划分为多个部分。处特定含义部分为0 ,| 运算就想当与加法
110000&000000 = 000000 可见&运算起到一个清0的作用
(
size
&
~
MODE_MASK
)
|
(
mode
&
MODE_MASK
);
和
size
+
mode
的作用是一样的只是位运算的算法效率更高
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
...
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
这两个方法通过位的与运算得到Mode和Size
/**
* Utility to return a default size. Uses the supplied size if the
* MeasureSpec imposed no constraints. Will get larger if allowed
* by the MeasureSpec.
*
* @param size Default size for this view
* @param measureSpec Constraints imposed by the parent
* @return The size this view should be.
*/
public static int getDefaultSize(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:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
第一个参数size 代表当 Mode为UNSPECIFIED是取的值
前2位代表mode,后30位代表size
进入View的onMeasure():
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
先看
setMeasuredDimension
()
方法
/**
* <p>This method must be called by {@link #onMeasure(int, int)} to store the
* measured width and measured height. Failing to do so will trigger an
* exception at measurement time.</p>
*
* @param measuredWidth The measured width of this view. May be a complex
* bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
* @param measuredHeight The measured height of this view. May be a complex
* bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
*/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
...
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
这个方法必须被onMeasure()调用,用来保存测量的宽高,否则在执行测量时会触发异常。measureWidth,measureHeight两个参数是MeasureSpec.getSize()的值。
应用试列:测量viewpager每一页的高度,使viewpager每个页面根据控件高度自适应
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.vcredit.vmoney.utils.CommonUtils;
import com.vcredit.vmoney.utils.ConstantUtils;
/**
* Created by qiubangbang on 2016/8/16.
*/
public class WrapContentViewpager extends ViewPager {
// private static final String TAG = "qbb_MyButton";
private int[] pageHeigeht = {0, 0, 0};
public WrapContentViewpager(Context context) {
super(context);
}
public WrapContentViewpager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
CommonUtils.LOG_D(getClass(), "inestmentpages: onMeasure");
CommonUtils.LOG_D(getClass(), "inestmentpages: onMeasure_item: " + getCurrentItem());
CommonUtils.LOG_D(getClass(), "inestmentpages: getchildCount: " + getChildCount());
// for (int i = 0; i < getChildCount(); i++) {
// View child = getChildAt(i);
// child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
// int h = child.getMeasuredHeight();
// if (h > height) {
// height = h;
// }
// }
View child;
if (getChildCount() > 0) {
//测量展示的viewpager页的高度
//只有pageheight没有值时采取测量
if (pageHeigeht[0] == 0 || pageHeigeht[1] == 0 || pageHeigeht[2] == 0) {
//点击时出现问题,如果不是按部就班滑动,而是直接点击第三页
if (pageHeigeht[1] == 0 && getCurrentItem() == 2) {
child = getChildAt(1);
} else if (pageHeigeht[2] != 0 && getCurrentItem() == 1) {
child = getChildAt(0);
} else {
if (getChildCount() == 2 && getCurrentItem() == 2) {
child = getChildAt(1);
} else {
child = getChildAt(getCurrentItem());
}
}
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
if (pageHeigeht[getCurrentItem()] == 0) {
pageHeigeht[getCurrentItem()] = child.getMeasuredHeight();
}
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(pageHeigeht[getCurrentItem()], MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}