Android 为我们提供了丰富的UI组件,同时也提供了方便的扩展方法,通过继承Android的系统组件,可以方便的扩展现有功能,本文总结下我的自定义控件学习历程。
首先从最简单的画个圆开始,自定义一个控件,显示一个蓝色的圆形,由于不需要扩展功能,所以新建一个类继承view,控件的绘制需要覆写onDraw()函数来绘制View的显示内容,重写类的构造方法,初始化Paint(相当于画笔)
public class myView extends View {
private float radius = 50;
private Paint mPaint;
public myView(Context context) {
super(context);
init();
}
public myView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public myView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setColor(Color.BLUE); //设置画笔颜色
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth(); //获取空间的宽高,以空间中心为圆心,以radius为半径画圆
int height = getHeight();
canvas.drawCircle(width/2,height/2,radius,mPaint);
}
}
<my.project.notificationtest.myView
android:layout_width="match_parent"
android:layout_height="100" />
当改变控件的宽高属性时,你会发现“wrap_content”和“match_content”绘制出来的圆形是一样的,都是按照“match_parent”的宽高来进行绘制的,实际上Android在绘制View前,必须要对View进行测量,即需要告诉系统画一个多大的View,Android系统为我们提供了三种测量模式:EXACTLY、AT_MOST、UNSPECIFIED:
EXACTLY:精准值模式,当控件layout_width属性或layout_height属性指定为具体数值或者为match_parent(View父控件的大小)时,使用EXACTLY模式,例如上面蓝色的圆测量的大小使用的是这种模式,使用不同数值,圆心位置也会发生变化。
AT_MOST:最大值模式,当控件的layout_width或layout_height属性指定为wrap_content时,控件大小随着子控件内容变化而变化,控件的尺寸只要不超过父控件最大尺寸即可
UNSPECIFIED:不指定大小测量模式,通常在绘制自定义View时使用。
测量的过程在onMeasure()方法中进行,默认的测量模式是EXACTLY,不重写onMeasure()方法就只能使用EXACTLY模式,所以我们绘制的圆形在设置宽高属性为wrap_content时没有变化,由于并没有给出确切的宽高,所以EXACTLY使用了match_parent的大小来绘制圆形,所以我们重写onMeasure();通过判断测量模式,给出不同的测量值,将测量的大小传给setMeasureDimension()方法。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMeasured,heightMeasured;
widthMeasured = measureWidth(widthMeasureSpec);
heightMeasured = measureHeight(heightMeasureSpec);
setMeasuredDimension(widthMeasured,heightMeasured);
}
private int measureHeight(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 200;
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
private int measureWidth(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if(specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{
result = 200;
if(specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
现在设置我们自定义空间的大小属性为wrap_content时,圆形控件的大小将变为200*200,圆点中心也发生变化。