[Android笔记]自定义控件系列一
2016-07-07 本文已影响84人
Zach_C
- 继承View必须要重写构造方法
- 构造方法中必须有继承一个和第二个
第二个构造方法中有AttributeSet参数,如果没有这个参数则会报RutimeException异常;
AttributeSet是用来解析android自带的layout_width、layout_height、id、padding、margin等属性的 - 笔 Paint
需要new Paint paint = new Patin(); - 纸 Canvas(画布)
纸在onDraw(Canvas canvas)方法中传递进来 - 不要在draw和layout的过程中去实例化对象
故不要在onDraw()方法中去实例化对象,一般在构造函数去实例化;
draw和layout很可能是一个频繁重复执行的过程,new是需要分配内存的,这样操作浪费内存,甚至爆掉 - Paint 各种属性
setAntiAlias(true) 抗锯齿
setColor() 设置画笔颜色
setStrokeWidth() 设置描边线条
setStyle()设置画笔样式
...
- Canvas 各种属性
drawCircle() 绘制圆
drawArc() 绘制圆弧
drawBitmap() 绘制位图
...
-
绘制一个圆环
-
测量工具类
public class MeasureUtil {
/**
* 获取屏幕尺寸
* @param activity
* @return 屏幕尺寸像素值,下标为0的值为宽度,下标为1的值为高
*/
public static int[] getScreenSize(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
return new int[]{metrics.widthPixels, metrics.heightPixels};
}
}
- 初始化画笔
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true); // 抗锯齿
/**
* 设置画笔样式
* 1. Paint.Style.STROKE: 描边
* 2. Paint.Style.FILL_AND_STROKE: 描边并填充
* 3. Paint.Style.FILL: 填充
*/
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.GREEN); // 设置画笔颜色
/**
* 设置画笔宽度,单位像素
* setStrokeWidth(0)的时候宽度并不为0,为一个像素
*/
mPaint.setStrokeWidth(10);
}
- 绘制圆
// 绘制圆
canvas.drawCircle(MeasureUtil.getScreenSize(((Activity) mContext))[0] / 2,
MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, 200, mPaint);
circle.png
圆已经出来了,现在我们来整个动态变换半径大小的怎么样,说干就干,不能怂。
其实要动态的圆很简单,我们只要更改其半径的大小,然后重绘就行,Android中给我们提供了invalidate()和postInvalidate()方法可以重绘我们的View,它们两者的区别我等下在下面叙述,我们先来看代码。
public class SimpleView extends View implements Runnable {
private Paint mPaint;
private Context mContext;
private int radius; // 半径
public SimpleView(Context context) {
this(context, null);
}
public SimpleView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true); // 抗锯齿
/**
* 设置画笔样式
* 1. Paint.Style.STROKE: 描边
* 2. Paint.Style.FILL_AND_STROKE: 描边并填充
* 3. Paint.Style.FILL: 填充
*/
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.GREEN); // 设置画笔颜色
/**
* 设置画笔宽度,单位像素
* setStrokeWidth(0)的时候宽度并不为0,为一个像素
*/
mPaint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制圆
canvas.drawCircle(MeasureUtil.getScreenSize(((Activity) mContext))[0] / 2,
MeasureUtil.getScreenSize((Activity) mContext)[1] / 2, radius, mPaint);
}
@Override
public void run() {
/**
* 不断刷新界面
*/
while (true) {
try {
/**
* 半径小于等于300则自动放大
*/
if(radius <= 300) {
radius += 20;
// 刷新view
postInvalidate();
} else {
radius = 0;
}
// 每100毫秒刷新一次
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
MainActivity中很简单,开个线程就行
simpleView = (SimpleView) findViewById(R.id.main_simpleview);
new Thread(simpleView).start();
dynamic.gif
如果我们把上面的postInvalidate改成invalidate就会报下面这个错,
Error1.png
这是为什么呢,因为我们在非UI线程中更新UI,而我们都知道在Android中非UI线程是不能更新UI的,故我们用postInvalidate。
postInvalidate()和invalidate()的区别:
invalidate()必须在主线程中调用,而postInvalidate()内部是由Handler的消息机制实现的,所以在任何线程中都可以调用,但实时性没有invalidate()强,一般为了保险起见,均使用postInvalidate()来刷新界面。