自定义view随笔填坑-----onMeasure
子view的大小由父view的measureSpec和子view的layoutparams共同决定。
view类的onMeasure():
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
widthMeasureSpec以及heightMeasureSpec的取值范围是由父view决定的
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
// ...省略大量代码
WindowManager.LayoutParams lp =mWindowAttributes;intdesiredWindowWidth;
int desiredWindowHeight;
intchildWidthMeasureSpec;
intchildHeightMeasureSpec;
// ...省略大量代码
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
// ...省略大量代码
// 根据前面的分析可知, 这里的 host 就是 DecorView
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// ...省略大量代码
}
private int getRootMeasureSpec(intwindowSize,introotDimension) {
int measureSpec;
switch(rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
一般自定view的时候,要在onMeasure对wrap_content进行处理
protected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
super.onMeasure(widthMeasureSpec , heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpceSize =MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
int heightSpceSize=MeasureSpec.getSize(heightMeasureSpec);if(widthSpecMode==MeasureSpec.AT_MOST&heightSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(mWidth, mHeight);
}else if(widthSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(mWidth, heightSpceSize);
}else if(heightSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(widthSpceSize, mHeight);
}
}
在viewGroup中测量子view时会调用到measureChildWithMargins(),或者与之类似的方法
// @param child 子view
// @param parentWidthMeasureSpec 父容器(比如LinearLayout)的宽的measurerSpec
// @params withUsed 父容器在水平方向已经占用的空间大小
//@params parentHeightMeasureSpec 父容器的高的MeasureSpec
//@params heightUsed 父容器在垂直方向已经占用的空间大小
protected void measureChildWithMargins(View child,intparentWidthMeasureSpec,intwidthUsed,intparentHeightMeasureSpec,intheightUsed) {
final MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child.getLayoutParams();
finalintchildWidthMeasureSpec =
getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight +
lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
final int childHeightMeasureSpec =
getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom +
lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
public static int getChildMeasureSpec(intspec,intpadding,intchildDimension) {
int specMode = View.MeasureSpec.getMode(spec);
intspec Size = View.MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize =0;
int resultMode =0;
switch(specMode) {caseView.MeasureSpec.EXACTLY:if(childDimension >=0) {
resultSize = childDimension;
resultMode = View.MeasureSpec.EXACTLY;
}elseif(childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = View.MeasureSpec.EXACTLY;
}elseif(childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = View.MeasureSpec.AT_MOST;
}break;caseView.MeasureSpec.AT_MOST:if(childDimension >=0) {
resultSize = childDimension;
resultMode = View.MeasureSpec.EXACTLY;
}elseif(childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = View.MeasureSpec.AT_MOST;
}elseif(childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = View.MeasureSpec.AT_MOST;
}break;caseView.MeasureSpec.UNSPECIFIED:if(childDimension >=0) {
resultSize = childDimension;
resultMode = View.MeasureSpec.EXACTLY;
}elseif(childDimension == LayoutParams.MATCH_PARENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ?0: size;
resultMode = View.MeasureSpec.UNSPECIFIED;
}elseif(childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ?0: size;
resultMode = View.MeasureSpec.UNSPECIFIED;
}break;
}returnView.MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}