首先我们自定义一个简单的控件,代码如下:
package com.osblog.androidrecipes;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class BullsEyeView extends View {
private final static String TAG = BullsEyeView.class.getSimpleName();
private Paint mPaint;
private Point mCenter;
private float mRadius;
public BullsEyeView(Context context) {
this(context, null);
}
public BullsEyeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BullsEyeView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
mCenter = new Point();
}
@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int width, height;
int contentWidth = 200;
int contentHeight = 200;
width = getMeasurement(widthMeasureSpec, contentWidth);
height = getMeasurement(heightMeasureSpec, contentHeight);
setMeasuredDimension(width, height);
}
private int getMeasurement(int measureSpec, int contentSize) {
int specSize = MeasureSpec.getSize(measureSpec);
switch (MeasureSpec.getMode(measureSpec)) {
case MeasureSpec.AT_MOST:
Log.v(TAG, specSize + "AT_MOST");
return Math.min(specSize, contentSize);
case MeasureSpec.UNSPECIFIED:
Log.v(TAG, "UNSPECIFIED");
return contentSize;
case MeasureSpec.EXACTLY:
Log.v(TAG, specSize + "EXACTLY");
return specSize;
default:
return 0;
}
}
@Override
protected void onSizeChanged(int w, int h,
int oldw, int oldh) {
if (w != oldw || h != oldh) {
mCenter.x = w / 2;
mCenter.y = h / 2;
mRadius = Math.min(mCenter.x, mCenter.y);
}
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setColor(Color.RED);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.8f, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.6f, mPaint);
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.4f, mPaint);
mPaint.setColor(Color.RED);
canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.2f,
mPaint);
}
}
然后在activity_bull布局文件中使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.osblog.androidrecipes.BullsEyeView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
运行的到如图效果
打印log日志为
02-01 11:15:43.310 11521-11521/com.osblog.androidrecipes V/BullsEyeView: 720EXACTLY
02-01 11:15:43.310 11521-11521/com.osblog.androidrecipes V/BullsEyeView: 1232EXACTLY
然后我们再将layout_width和layout_height改为wrap_content
运行效果:
打印log如下:
02-01 11:31:10.080 1886-1886/com.osblog.androidrecipes V/BullsEyeView: 720AT_MOST
02-01 11:31:10.080 1886-1886/com.osblog.androidrecipes V/BullsEyeView: 1160AT_MOST
然后我们再将layout_width和layout_height改为100dp
运行效果和上图一样,打印log日志为
02-01 11:32:35.440 4006-4006/com.osblog.androidrecipes V/BullsEyeView: 200EXACTLY
02-01 11:32:35.440 4006-4006/com.osblog.androidrecipes V/BullsEyeView: 200EXACTLY
通过查看官方API MeasureSpec有3种mode分别
-
UNSPECIFIED
- The parent has not imposed any constraint on the child. It can be whatever size it wants. EXACTLY
- 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. AT_MOST
- The child can be as large as it wants up to the specified size.
通过log和代码分析总结如下:
MeasureSpec.EXACTLY:当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="100dip",或者为match_patent(fill_parent),都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为wrap_content时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,既控件可以得到它想要的最大尺寸,这种情况不多,一般都是父控件是AdapterView(listview或者scrollview),通过measure方法传入的模式。
通过代码我们可以看到当测量完成之后通过setMeasureDimension(weith,height)设置控件的宽高,在 onSizeChanged方法中已经得到了具体的宽高,我们在这里可以进行相关操作,例如本例我们计算画圆的圆心。
代码从上而下的三个构造方法,第一个是当我们使用 setContentView(new BullsEyeView())的时候调用,既直接用java代码new一个view。
第二个是我们在布局文件中调用,第三个是在布局文件中并且使用了一个style。