想要写自定义控件,必须清楚控件的绘制原理,幸好,幸好,这本书让我对view的理解更清晰了
View的测量
.1. 我们清楚view的测量是在onMeasure()方法中完成的,测量模式,API提供了三种:
EXACTLY:
精确模式,控件的layout_width,layout_height,为确定值,或者match_parentAT_MOST:
最大模式,控件的layout_width,layout_height,为wrap_content,此时控件的大小随着控件的子空间或内容改变,但是控件尺寸不能超过父控件允许的最大尺寸。UNSPECIFIED:
想多大就多大,通常在自定义VIEW时使用
.2. View默认的onMeasure只支持EXACTLY模式,如果不重写onMeasure方法,只能使用EXACTLY模式。控件可以响应指定的具体宽高,或者match_parent,而如果让自定义view支持wrap_content,必须重写onMeasure指定wrap_content时的大小
(新技能get,哈哈哈)
.3. 参看super.onMeasure源码,可知
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
因此,需要重写measureWidth(int measureWidth)方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measuredWidth(widthMeasureSpec), measuredWidth(heightMeasureSpec));
}
private int measuredWidth(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;
}
设置layout_height,layout_width为wrap_content效果如下图:
设置layout_height=500dp,layout_width=match_parent,效果如下图:
View的绘制
view测量好之后,就要在onDraw(Canvas canvas)方法上绘制
.1.创建Canvas 对象
Canvas canvas = new Canvas(bitmap)
传进去的bitmap与创建的canvas画布联系在一起,称之为装载画布。这个bitmap用来存储所有绘制在canvas上的像素信息,后面调用的canvas.drawxxx都作用在这个bitmap上。下面的代码可以证实canvas与bitmap的直接关系
canvas.drawBitmap(bitmap1,0,0,null);
Canvas mcanvas = new Canvas(bitmap1);
mcanvas.drawxxx
通过这个mcanvas将绘制效果作用在bitmap1上,在刷新view的时候,就会发现通过onDraw方法绘制出来的bitmap1已经发生了变化,这就是bitmap1承载了mcanvas上所进行的绘图操作,我们其实并没有绘制在mcanvas画布上,而是通过改变bitmap,然后让view重绘,从而改变之后的bitmap
(又get新技能,o(∩_∩)o 哈哈)