今天学习了一下自定义view,根据提供的官方文档。
首先,我建立了一个自定义的class文件(Customview),继承View。然后我们对构造函数进行重写。这里问题来了:如果我们重写以这种方式
public CustomView(Context context) {
super(context,null);
}
然后对xml去引用咱们自定的view
如
那么就会报错。 为什么呢?在xml文件引用我们的CustomView类时为其指定了两个android自带的两个属性:layout_width和layout_height,当我们需要使用类似的属性(比如更多的什么id啊、padding啊、margin啊之类)时必须在自定义View的构造方法中添加一个AttributeSet类型的签名来解析这些属性:
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
ok,接下来添加一些东西,我们需要做的是创建画布和画笔
关于画布Canvas,我们可以看到在onDraw方法中,画布Canvas作为签名被传递进来,也就是说这个画布是Android为我们准备好的,不需要你去管.所以我们只需要设置画笔相关的就ok了。Paint类。这里为了节省性能,防止画笔反复被创建,我们在外部声明,用init方法去初始化他。
代码块
public class CustomView extends View {
private Paint mPaint;
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化画笔
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
// 实例化画笔并打开抗锯齿
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
我们可以用 画笔对象名.setXXX()的方式去为我们的画笔设置一些颜色,样式等。
然后在onDraw方法里对图像绘制。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制圆环
canvas.drawCircle(float x, float y, 200, mPaint);
}
x,y分别是需要传入的float形参数,表示圆心的坐标,第三个是半径,第四个是画笔对象。
不错吧?我们的第一个view完成啦。
在Android中提供了一个叫invalidate()的方法来让我们重绘我们的View。利用它,我们好像可以让view动起来。
我们可以在主线程中刷新它,但是好像过于繁琐,我们直接用它implent runnable 接口试试。
我们修改一下代码
public class CustomView extends View implements Runnable {
private Paint mPaint;// 画笔
private Context mContext;// 上下文环境引用
private int radiu;// 圆环半径
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// 初始化画笔
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
// 实例化画笔并打开抗锯齿
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/*
* 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
*
* 画笔样式分三种:
* 1.Paint.Style.STROKE:描边
* 2.Paint.Style.FILL_AND_STROKE:描边并填充
* 3.Paint.Style.FILL:填充
*/
mPaint.setStyle(Paint.Style.STROKE);
// 设置画笔颜色为浅灰色
mPaint.setColor(Color.LTGRAY);
/*
* 设置描边的粗细,单位:像素px
* 注意:当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, radiu, mPaint);
}
@Override
public void run() {
/*
* 确保线程不断执行不断刷新界面
*/
while (true) {
try {
/*
* 如果半径小于200则自加否则大于200后重置半径值以实现往复
*/
if (radiu <= 200) {
radiu += 10;
// 刷新View,不能直接用invalid,因为主线程才能刷新ui
postInvalidate()
} else {
radiu = 0;
}
// 每执行一次暂停40毫秒
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这里将postInvalid()我们不去深究,应该是通知主线程去刷了,但是这样开发会降低耦合? 个人观点。这样就实现了动画view开发!!!
但是我也有一些疑问如下,
context到底是什么用?
-----------------------分界线-----------------------
栋哥告诉我,最好不要把组件放在线程里,这是典型的内存泄漏场景。好吧,老老实实用handler吧。