第一步:画一个简单的圆
第二步:绘制刻度
第三步:绘制时,分,表指针
第四步:绘制当前时间文字
第五步:实现时间动态显示
第一步画一个圆是很简单的,
package com.example.clockview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Created by Adminis on 2016/11/6.
*/
public class ClockView extends ImageView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;//控件的宽度
private int height = 200;//控件的高度
private int padding = 5;
public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth, height);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#666666"));
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas);
}
/**
* 绘制圆
* @param canvas
*/
private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth/2,height/2,widhth/2-padding,mPaint);
}
}
效果图:
第二步:绘制刻度
/**
* 绘制刻度
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);
}
效果图:
但是我们要画类似这种图:
一起要画12个这个线,那就相当于每二根线之间的角度就是360/12=30度
分析如图:
每换完一根线后画布就旋转30度
/**
* 绘制刻度
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++){
if(i%3==0){// 12 3 6 9对应的线长点
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);
}else{
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);
}
canvas.rotate(30, widhth / 2, widhth / 2);
}
}
效果:
刻度就是利用了canvas的rotate()旋转方法绘制上去的,但是得以圆的中心点为旋转点
第三步绘制时 分 表 表针
这个绘制是根据当前的时间来指向的,
/**
* 绘制时 分 表 指针
* @param canvas
*/
private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance();
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
//小时的旋转度
mDegrees = mHour*30+mMinuate/2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
canvas.restore();
//分钟
mPaint.setColor(Color.RED);
mDegrees = mMinuate*6+mSecond/10;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
canvas.restore();
//绘制表针
mPaint.setColor(Color.BLUE);
mDegrees = mSecond*6;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
canvas.restore();
}
效果图:
在这解释下这个mDegrees这个值 比如现在是21:20 但是分为12小时制的话就是9:20 时就是9*30度=270度 但是这个时针指向9肯定是不对的,因为还有20分钟呢?时针线肯定是在9和10之间指向,这么这1小时之间是30度,就拿21:30这个时间来算,这个时候时针角度应该是30*9+30/2=285度 这就说明60分钟1小时 而1小时是30度,也就是说每2分钟时针角度应该动一下,根据这个原理分钟和表之间的计算也是类似的,
还有一点要特别的注意2就是绘制完时针它是根据当前的时针数比如9然后旋转30*9的 但是canvas使用了动画完以后一定要恢复到原来的,不然你绘制分钟就会出问题,我就遇到了这个问题,在这特别提醒下
特别改动下了:类全部代码复制下看下效果:
package com.example.clockview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.ImageView;
import java.util.Calendar;
/**
* Created by Adminis on 2016/11/6.
*/
public class ClockView extends ImageView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;//控件的宽度
private int height = 200;//控件的高度
private int padding = 5;
private Calendar mCalendar;
private int mHour;//小时
private int mMinuate;//分钟
private int mSecond;//秒
private float mDegrees ;//因为圆是360度 我们有12个刻度 所以就是360/12
private int mHourLineLen;//时指针 线
private int mMinuateLine;//分钟 线
private int mSecondLine ;//表钟线
public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth, height);
mHourLineLen = (int) (widhth/2*0.6);
mMinuateLine = (int) (widhth/2*0.7);
mSecondLine = (int) (widhth/2*0.8);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#666666"));
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas);
drawScale(canvas);
canvasCenterCircle(canvas);
drawPointer(canvas);
}
/**
* 在 圆中心绘制一个点
* @param canvas
*/
private void canvasCenterCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(widhth / 2, height / 2, 5, mPaint);
}
/**
* 绘制圆
* @param canvas
*/
private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth / 2, height / 2, widhth / 2 - padding, mPaint);
}
/**
* 绘制刻度
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++){
if(i%3==0){// 12 3 6 9对应的线长点
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);
}else{
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);
}
canvas.rotate(30, widhth / 2, widhth / 2);
}
}
/**
* 绘制时 分 表 指针
* @param canvas
*/
private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance();
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
//小时的旋转度
mDegrees = mHour*30+mMinuate/2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
canvas.restore();
//分钟
mPaint.setColor(Color.parseColor("#666666"));
mPaint.setStrokeWidth(5);
mDegrees = mMinuate*6+mSecond/10;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mMinuateLine, mPaint);
canvas.restore();
//绘制表针
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.parseColor("#666666"));
mDegrees = mSecond*6;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mSecondLine, mPaint);
canvas.restore();
}
}
效果:
第四步就是绘制当前时间文字显示在中心点下面:
/**
* 绘制文字
* @param canvas
*/
private void drawStr(Canvas canvas) {
mPaint.setTextSize(24);
StringBuffer sb = new StringBuffer();
if(mHour<10){
sb.append("0").append(String.valueOf(mHour)).append(":");
}else{
sb.append(String.valueOf(mHour)).append(":");
}
if(mMinuate<10){
sb.append("0").append(String.valueOf(mMinuate)).append(":");
}else{
sb.append(String.valueOf(mMinuate)).append(":");
}
if(mSecond<10){
sb.append("0").append(String.valueOf(mSecond));
}else{
sb.append(String.valueOf(mSecond));
}
String str = sb.toString();
int strW = (int) mPaint.measureText(str);
canvas.drawText(str, widhth / 2 - strW / 2, widhth / 2 + 30,mPaint);
}
效果:
最后一步就是利用Handler每秒去刷新界面就能做到动态的显示时间了:
最后完整的代码:
package com.example.clockview;
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.widget.ImageView;
import java.util.Calendar;
/**
* Created by Adminis on 2016/11/6.
*/
public class ClockView extends ImageView {
private static final String TAG = "ClockView";
private Paint mPaint;
private int widhth = 200;//控件的宽度
private int height = 200;//控件的高度
private int padding = 5;
private Calendar mCalendar;
private int mHour;//小时
private int mMinuate;//分钟
private int mSecond;//秒
private float mDegrees ;//因为圆是360度 我们有12个刻度 所以就是360/12
private int mHourLineLen;//时指针 线
private int mMinuateLine;//分钟 线
private int mSecondLine ;//表钟线
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
invalidate();
}
};
public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widhth, height);
mHourLineLen = (int) (widhth/2*0.6);
mMinuateLine = (int) (widhth/2*0.7);
mSecondLine = (int) (widhth/2*0.8);
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#666666"));
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
drawCircle(canvas);
drawScale(canvas);
canvasCenterCircle(canvas);
drawPointer(canvas);
drawStr(canvas);
mHandler.sendEmptyMessage(1);
}
/**
* 绘制文字
* @param canvas
*/
private void drawStr(Canvas canvas) {
mPaint.setTextSize(24);
StringBuffer sb = new StringBuffer();
if(mHour<10){
sb.append("0").append(String.valueOf(mHour)).append(":");
}else{
sb.append(String.valueOf(mHour)).append(":");
}
if(mMinuate<10){
sb.append("0").append(String.valueOf(mMinuate)).append(":");
}else{
sb.append(String.valueOf(mMinuate)).append(":");
}
if(mSecond<10){
sb.append("0").append(String.valueOf(mSecond));
}else{
sb.append(String.valueOf(mSecond));
}
String str = sb.toString();
int strW = (int) mPaint.measureText(str);
canvas.drawText(str, widhth / 2 - strW / 2, widhth / 2 + 30,mPaint);
}
/**
* 在 圆中心绘制一个点
* @param canvas
*/
private void canvasCenterCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(widhth / 2, height / 2, 5, mPaint);
}
/**
* 绘制圆
* @param canvas
*/
private void drawCircle(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(widhth / 2, height / 2, widhth / 2 - padding, mPaint);
}
/**
* 绘制刻度
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++) {
if (i % 3 == 0) {// 12 3 6 9对应的线长点
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);
}else{
canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);
}
canvas.rotate(30, widhth / 2, widhth / 2);
}
}
/**
* 绘制时 分 表 指针
* @param canvas
*/
private void drawPointer(Canvas canvas) {
mCalendar = Calendar.getInstance();
mHour = mCalendar.get(Calendar.HOUR);
mMinuate = mCalendar.get(Calendar.MINUTE);
mSecond = mCalendar.get(Calendar.SECOND);
//小时的旋转度
mDegrees = mHour*30+mMinuate/2;
mPaint.setColor(Color.BLACK);
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
canvas.restore();
//分钟
mPaint.setColor(Color.parseColor("#666666"));
mPaint.setStrokeWidth(5);
mDegrees = mMinuate*6+mSecond/10;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mMinuateLine, mPaint);
canvas.restore();
//绘制表针
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.parseColor("#666666"));
mDegrees = mSecond*6;
canvas.save();
canvas.rotate(mDegrees, widhth / 2, widhth / 2);
canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mSecondLine, mPaint);
canvas.restore();
}
}
动态效果:
终于写完了!
刚群里有人反映说3时和15秒时时针和表针线不重合的问题,于是我自己写死了一个数据 测试了下发现真的是有这个bug 导致这个bug造成的原因是
/**
* 绘制刻度
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++) {
if (i % 3 == 0) {// 12 3 6 9对应的线长点
canvas.drawLine(widhth / 2-padding , padding, widhth / 2 -padding, padding + 4 + 15, mPaint);
}else{
canvas.drawLine(widhth / 2-padding , padding, widhth / 2-padding, padding + 4 + 8, mPaint);
}
canvas.rotate(30, widhth / 2, widhth / 2);
}
}
是这个x轴开始和结束点的坐标不对,应该把-padding去掉就正常了
改成后:
/**
* 绘制刻度
* @param canvas
*/
private void drawScale(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
for(int i=0;i<12;i++) {
if (i % 3 == 0) {// 12 3 6 9对应的线长点
canvas.drawLine(widhth / 2-padding , padding, widhth / 2 -padding, padding + 4 + 15, mPaint);
}else{
canvas.drawLine(widhth / 2-padding , padding, widhth / 2-padding, padding + 4 + 8, mPaint);
}
canvas.rotate(30, widhth / 2, widhth / 2);
}
}
我现在把时针写死在3点 秒数定在15秒后效果: