public class CircleView extends View {
private Paint mPaint;
private float cx;
private float cy;
//这个是外部通过new 的方式调用
public CircleView(Context context) {
super(context);
}
//这个是外部通过xml 的方式调用
public CircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
...
}
private void init() {
mPaint = new Paint();
//抗锯齿
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
}
@Override
protected void onDraw(Canvas canvas) {
//super内部空实现
super.onDraw(canvas);
cx = getMeasuredWidth()/2;
cy = getMeasuredHeight()/2;
float radius = Math.min(cx,cy);
radius = radius-5;
//画了个圆
canvas.drawCircle(cx,cy,radius,mPaint);
}
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.okay.myapplication.CircleView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("CircleView", MeasureSpec.getSize(widthMeasureSpec)+":"+ MeasureSpec.getSize(heightMeasureSpec));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
这里先留下个问题,就是onMeasure方法走了两次,why?
先不管那个,我们看下源码是怎么帮我们测量view的宽高的。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
/**
* @return The suggested minimum width of the view.
* 得到最小的宽度,如果设置了Background,取最小宽度和背景的最小宽度之中的最大值
*/
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
/**
* 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) {
//不能确定子view的大小
case MeasureSpec.UNSPECIFIED:
result = size;
break;
//最大能给子孩子的大小尺寸,也就是父控件的最大尺寸specSize
case MeasureSpec.AT_MOST:
//子孩子在xml里写的尺寸是固定的,android:layout_width="50dp"
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
getChildMeasureSpec关键方法,负责childWidthMeasureSpec值的拼装,lp.width就是childDimension,也就是我们在xml里android:layout_width写的值。
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("CircleView", MeasureSpec.getSize(widthMeasureSpec)+":"+ MeasureSpec.getSize(heightMeasureSpec));
Log.d("CircleView", MeasureSpec.getMode(widthMeasureSpec)+":"+ MeasureSpec.getMode(heightMeasureSpec));
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getSize(50,widthMeasureSpec),getSize(50,heightMeasureSpec));
Log.d("CircleView", getMeasuredWidth()+":"+ getMeasuredHeight());
}
public static int getSize(int minSize, int measureSpec) {
int result = minSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
result = Math.min(minSize,specSize);
break;
//Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
default:
result = specSize;
break;
}
return result;
}