自定义View
自定义View的一些知识
自定义一个类继承View,然后将这个View添加到布局文件中 ,然后在活动中对这个View进行UI操作。在onDraw方法中需要用到Canvas来画各种形状的图形。Canvas的方法中还涉及到一个API:Paint,代表了Canvas上面的画笔,因此Paint类主要用于设置绘制风格,包括画笔颜色、画笔笔触粗细、填充风格等,常用的方法例:
- setColor(int color):设置颜色
- setAntiAlias(boolean aa):设置是否抗锯齿(true则抗锯齿)
- setStrokeWidth(float width):设置线宽
- setStyle(Paint.Style style):设置风格,例setStyle(Paint.Style.STROKE)设置为无填充
setTextSize(float textSize):设置字体大小
另外Canvas还提供了一些方法进行坐标变换,如
- rotate(float degrees,float px,float py):对Canvas进行旋转变换
- scale(float sx,float sy,float px,float py): 对Canvas进行缩放变换
- skew(float sx,float sy):对Canvas执行倾斜变换
translate(float dx,float dy):移动Canvas (正数:向右移动dx距离,向下移动dy距离;负数:相反)
自定义View的步骤:
- 新建一个类继承View
- 继承一个构造方法(根据自己的需要,此处选的是两个参数的那个构造
器和一个参数的那个构造器) - 重写onMesure
- 重写onDraw
时钟Demo
其实这玩意也挺麻烦的。。。这家伙数字还没有调过来呢,要调的话太麻烦,将就用了╮(╯▽╰)╭。
实现这个钟表大体分为4步:
- 新建类继承View,重写俩个方法onDraw和onMeasure,继承两个构造方法
- 绘制钟表
- 使钟表动起来
- 将组件添加到布局中
绘制钟表
在onMeasure中设置长度宽度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWith = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
mHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
}
在两个参数的构造器中设置画笔paint
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
mLinePaint=new Paint();
mLinePaint.setColor(Color.BLACK);
mLinePaint.setStrokeWidth(5);//线宽
mLinePaint.setAntiAlias(true);//抗锯齿
mLineSecondPaint=new Paint();
mLineSecondPaint.setColor(Color.BLACK);
mLineSecondPaint.setStrokeWidth(2);//线宽
mLineSecondPaint.setAntiAlias(true);//抗锯齿
mCirclePaint=new Paint();
mCirclePaint.setColor(Color.BLACK);
mCirclePaint.setStrokeWidth(5);//线宽
mCirclePaint.setStyle(Paint.Style.STROKE);//空心圆
mCirclePaint.setAntiAlias(true);//抗锯齿
mCircleText=new Paint();
mCircleText.setTextSize(30);//字体大小
mCircleText.setTextAlign(Paint.Align.CENTER);//对齐方式
}
在onDraw中画出图形,需要注意的是其中整数相除的时候必须要造型成浮点型数据,不然会出错。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// onDraw是由UI主线程自动调用,只需要再此绘制即可
// canvas.drawLine(0, 0, 222, 222, mLinePaint);
canvas.drawCircle(mWith / 2, mHeight / 2, 200, mCirclePaint);
canvas.drawCircle(mWith / 2, mHeight / 2, 2, mCirclePaint);
for(int i=1;i<=12;i++) {
canvas.save();//保存当前画布状态
canvas.rotate(360 / 12 * i, mWith / 2, mHeight / 2);//旋转,先旋转再画(旋转角度,旋转中心点xy坐标)
canvas.drawLine(mWith / 2, mHeight / 2 - 200, mWith / 2, mHeight / 2 - 180, mLinePaint);
canvas.drawText("" + i, mWith / 2, mHeight / 2 - 150, mCircleText);
canvas.restore();//恢复到原位置
}
mCalendar=Calendar.getInstance();
// 获得时分秒
int minute=mCalendar.get(Calendar.MINUTE);
int hour=mCalendar.get(Calendar.HOUR);
int second=mCalendar.get(Calendar.SECOND);
// 画上分针
float degree=minute/60f*360;//必须定义成浮点型数据,
canvas.save();
canvas.rotate(degree, mWith / 2, mHeight / 2);//将下面line旋转这些角度
canvas.drawLine(mWith / 2, mHeight / 2 - 120, mWith / 2, mHeight / 2 +20, mLinePaint);
canvas.restore();
// 画上时针
float hourDegree=(hour*60+minute)/(12*60f)*360;
canvas.save();
canvas.rotate(hourDegree, mWith / 2, mHeight / 2);
canvas.drawLine(mWith / 2, mHeight / 2 - 90, mWith / 2, mHeight / 2 +20, mLinePaint);
canvas.restore();
// 画上秒针
float secondDegree=second/60f*360;//加f的位置要注意- -
canvas.save();
canvas.rotate(secondDegree, mWith / 2, mHeight / 2);
canvas.drawLine(mWith / 2, mHeight / 2 - 140, mWith / 2, mHeight / 2 + 20, mLineSecondPaint);
canvas.restore();
}
让钟表动起来,通过Handler实现
在两个参数的构造器 MyView(Context context, AttributeSet attrs)中发送信息
handler.sendEmptyMessageDelayed(CLOCKFLUSH,1000);
接收消息并处理,在handler中再发送消息形成循环
public static final int CLOCKFLUSH=0X23;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case CLOCKFLUSH:
mCalendar=Calendar.getInstance();//重新获取当前时间
invalidate();//刷新
handler.sendEmptyMessageDelayed(CLOCKFLUSH,1000);
break;
default:
break;
}
}
};
布局中只添加了这个组件
<com.example.laowang.android0916canvas_.weight.MyView
android:layout_width="match_parent"
android:layout_height="match_parent" />
实现效果(请无视这个按键。。。)
全部代码View类
package com.example.laowang.android0916canvas_.weight;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import java.util.Calendar;
/**
* Created by Administrator on 2015/9/16.
*/
public class MyView extends View {
private int mWith;
private int mHeight;
private Paint mLinePaint;
private Paint mLineSecondPaint;
private Paint mCirclePaint;
private Paint mCircleText;
private Calendar mCalendar;
public static final int CLOCKFLUSH=0X23;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case CLOCKFLUSH:
mCalendar=Calendar.getInstance();//重新获取当前时间
invalidate();//刷新
handler.sendEmptyMessageDelayed(CLOCKFLUSH,1000);
break;
default:
break;
}
}
};
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
mLinePaint=new Paint();
mLinePaint.setColor(Color.BLACK);
mLinePaint.setStrokeWidth(5);//线宽
mLinePaint.setAntiAlias(true);//抗锯齿
mLineSecondPaint=new Paint();
mLineSecondPaint.setColor(Color.BLACK);
mLineSecondPaint.setStrokeWidth(2);//线宽
mLineSecondPaint.setAntiAlias(true);//抗锯齿
mCirclePaint=new Paint();
mCirclePaint.setColor(Color.BLACK);
mCirclePaint.setStrokeWidth(5);//线宽
mCirclePaint.setStyle(Paint.Style.STROKE);//空心圆
mCirclePaint.setAntiAlias(true);//抗锯齿
mCircleText=new Paint();
mCircleText.setTextSize(30);//字体大小
mCircleText.setTextAlign(Paint.Align.CENTER);//对齐方式
handler.sendEmptyMessageDelayed(CLOCKFLUSH,1000);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWith = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
mHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// onDraw是由UI主线程自动调用,只需要再此绘制即可
// canvas.drawLine(0, 0, 222, 222, mLinePaint);
canvas.drawCircle(mWith / 2, mHeight / 2, 200, mCirclePaint);
canvas.drawCircle(mWith / 2, mHeight / 2, 2, mCirclePaint);
for(int i=1;i<=12;i++) {
canvas.save();//保存当前画布状态
canvas.rotate(360 / 12 * i, mWith / 2, mHeight / 2);//旋转,先旋转再画(旋转角度,旋转中心点xy左边)
canvas.drawLine(mWith / 2, mHeight / 2 - 200, mWith / 2, mHeight / 2 - 180, mLinePaint);
canvas.drawText("" + i, mWith / 2, mHeight / 2 - 150, mCircleText);
canvas.restore();//恢复到原位置
}
mCalendar=Calendar.getInstance();
// 获得时分秒
int minute=mCalendar.get(Calendar.MINUTE);
int hour=mCalendar.get(Calendar.HOUR);
int second=mCalendar.get(Calendar.SECOND);
// 画上分针
float degree=minute/60f*360;//必须定义成浮点型数据,
canvas.save();
canvas.rotate(degree, mWith / 2, mHeight / 2);//将下面line旋转这些角度
canvas.drawLine(mWith / 2, mHeight / 2 - 120, mWith / 2, mHeight / 2 +20, mLinePaint);
canvas.restore();
// 画上时针
float hourDegree=(hour*60+minute)/(12*60f)*360;
canvas.save();
canvas.rotate(hourDegree, mWith / 2, mHeight / 2);
canvas.drawLine(mWith / 2, mHeight / 2 - 90, mWith / 2, mHeight / 2 +20, mLinePaint);
canvas.restore();
// 画上秒针
float secondDegree=second/60f*360;//加f的位置要注意- -
canvas.save();
canvas.rotate(secondDegree, mWith / 2, mHeight / 2);
canvas.drawLine(mWith / 2, mHeight / 2 - 140, mWith / 2, mHeight / 2 + 20, mLineSecondPaint);
canvas.restore();
}
}