Android绘制时钟

来源:互联网 发布:打印模板软件 编辑:程序博客网 时间:2024/05/21 15:41

最近在学View相关的知识,对Canvas和Paint类有了初步了解,看到别人弄了一个会走动的时钟,自己也打算给弄上一个,算是巩固一下相关知识。效果图如下:




首先来分析一下,要绘制出一个时钟,要绘制哪几部分:时钟的大圆、圆上的刻度线、圆外的数字、圆中心处的圆点以及三个时钟指针。既然明白了要绘制的东西,下面只需要想办法来实现即可。

(1)绘制时钟的大圆

这个最简单,只要确定好圆中心坐标点和半径即可

(2)绘制圆上的刻度线

一个时钟总共60个刻度线,通过canvas的rotate()方法,进行不断的旋转,每次旋转的角度为360/60;

(3)绘制圆外的数字

和上面一样,进行旋转绘制即可;

(4)绘制圆中心处的圆点

和(1)一样,只是注意更改一下画笔的设置;

(5)绘制三个时钟指针

根据当前的时分秒,计算出每个指针的旋转角度,在直接绘制线段即可;


注意到,时钟是每秒走一次,也即我们每秒就要刷新一次界面,重复一次上面的绘制工作。所以,一初始化时钟View的时刻,我们通过Handler弄一个定时器,每秒进行刷新操作。


ClockView源码如下:

package com.scu.lly.drawclock.view;import java.util.Calendar;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Handler;import android.util.AttributeSet;import android.view.View;public class ClockView extends View {private Paint mPaint;/** * 描边线的粗细 */private int strokeWidth = 2;/** * 时钟是否在走(即是否第一次onDraw) */private boolean isRunning;private Handler mHandler;private Runnable clockRunnable;/** * 时钟圆的半径 */private int radius = 150;private String[] clockNumbers = {"12","1","2","3","4","5","6","7","8","9","10","11"};/** * 时钟上需要绘制的数字 */private String num;/** * 用于测量文本的宽、高度(这里主要是来获取高度) */private Rect textBounds = new Rect();private Calendar cal;private int hour,min,second;private float hourAngle,minAngle,secAngle;public ClockView(Context context, AttributeSet attrs) {super(context, attrs);init();}public ClockView(Context context) {super(context);init();}private void init() {mPaint = new Paint();mHandler = new Handler();//cal = Calendar.getInstance();clockRunnable = new Runnable() {//里面做的事情就是每隔一秒,刷新一次界面@Overridepublic void run() {//线程中刷新界面postInvalidate();mHandler.postDelayed(this, 1000);}};}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if(!isRunning){runClock();}else{initPaint();//绘制圆形部分drawClockCircle(canvas);//绘制刻度线drawClockScale(canvas);//绘制数字drawClockNumber(canvas);//绘制中心原点drawClockDot(canvas);//绘制三个指针drawClockPoint(canvas);}}/** * 绘制三个指针 * @param canvas */private void drawClockPoint(Canvas canvas) {cal = Calendar.getInstance();hour = cal.get(Calendar.HOUR);//Calendar.HOUR获取的是12小时制,Calendar.HOUR_OF_DAY获取的是24小时制min = cal.get(Calendar.MINUTE);second = cal.get(Calendar.SECOND);//计算时分秒指针各自需要偏移的角度hourAngle = (float)hour / 12 * 360 + (float)min / 60 * (360 / 12);//360/12是指每个数字之间的角度minAngle = (float)min / 60 * 360;secAngle = (float)second / 60 * 360;//下面将时、分、秒指针按照各自的偏移角度进行旋转,每次旋转前要先保存canvas的原始状态canvas.save();canvas.rotate(hourAngle,getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - 65, mPaint);//时针长度设置为65canvas.restore();canvas.save();canvas.rotate(minAngle,getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - 90 , mPaint);//分针长度设置为90canvas.restore();canvas.save();canvas.rotate(secAngle,getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - 110 , mPaint);//秒针长度设置为110canvas.restore();}/** * 绘制中心原点 */private void drawClockDot(Canvas canvas) {mPaint.reset();//mPaint.setAntiAlias(true);mPaint.setColor(Color.RED);mPaint.setStyle(Paint.Style.FILL);canvas.drawCircle(getWidth() / 2, getHeight() / 2, 6, mPaint);initPaint();}/** * 绘制数字(从正上方12点处开始绘制) * @param canvas */private void drawClockNumber(Canvas canvas) {//先保存一下当前画布的状态,因为后面画布会进行旋转操作,而在绘制完刻度后,需要恢复画布状态canvas.save();//mPaint.setStrokeWidth(2);mPaint.setTextSize(25);//计算12点处 数字 的坐标int preX = getWidth() / 2;int preY = getHeight() / 2 - radius - strokeWidth - 10;//10为圆与数字文本之间的间距//x,y才是文本真正的准确坐标,需要减去文本的自身宽、高因素int x,y;//计算画布每次需要旋转的角度int degree = 360 / clockNumbers.length;for(int i = 0; i < clockNumbers.length; i++){num = clockNumbers[i];mPaint.getTextBounds(num, 0, num.length(), textBounds);x = (int) (preX - mPaint.measureText(num) / 2);y = preY - textBounds.height();//从文本的中心点处开始绘制canvas.drawText(num, x, y, mPaint);canvas.rotate(degree, getWidth() / 2, getHeight() / 2);//以圆中心进行旋转}//绘制完后,记得把画布状态复原canvas.restore();}/** * 绘制刻度线(总共60条) * 从正上方,即12点处开始绘制一条直线,后面的只是旋转一下画布角度即可 * @param canvas */private void drawClockScale(Canvas canvas) {//先保存一下当前画布的状态,因为后面画布会进行旋转操作,而在绘制完刻度后,需要恢复画布状态canvas.save();//计算12点处刻度的开始坐标int startX = getWidth() / 2;int startY = getHeight() / 2 - radius;//y坐标即园中心点的y坐标-半径//计算12点处的结束坐标int stopX = startX;int stopY1 = startY + 30;//整点处的线长度为30int stopY2 = startY + 15;//非整点处的线长度为15//计算画布每次旋转的角度float degree = 360 / 60;for(int i = 0; i < 60; i++){if(i % 5 == 0)canvas.drawLine(startX, startY, stopX, stopY1, mPaint);//绘制整点长的刻度elsecanvas.drawLine(startX, startY, stopX, stopY2, mPaint);//绘制非整点处短的刻度canvas.rotate(degree, getWidth() / 2, getHeight() / 2);//以圆中心进行旋转}//绘制完后,记得把画布状态复原canvas.restore();}/** * 绘制时钟的圆形部分 * @param canvas */private void drawClockCircle(Canvas canvas) {//获得圆的圆点坐标int x = getWidth() / 2;int y = getHeight() / 2;canvas.drawCircle(x, y, radius, mPaint);}private void initPaint() {mPaint.reset();mPaint.setColor(Color.RED);mPaint.setStyle(Paint.Style.STROKE);//设置描边mPaint.setStrokeWidth(strokeWidth);//设置描边线的粗细mPaint.setAntiAlias(true);//设置抗锯齿,使圆形更加圆滑}private void runClock() {isRunning = true;mHandler.postDelayed(clockRunnable, 1000);}}

注:上面一些计算只要把草图画好,计算坐标就好。


缺陷:

从效果图中可以看到一个明显的不足就是,绘制的数字按一个方向旋转后,效果不满意。我尝试了分段绘制,比如将12个数字分成四组{12,,1,2},{3,4,5},{6,7,8},{9,10,11},还是不尽人意。我最主要的问题就是卡在canvas的rotate()旋转方法的理解上,查了很多资料,基本上认同的是旋转的坐标,看时钟的效果也的确如此,但是目前主要是对rotate方法理解不够深入,不能进行灵活应用。

有优化的方案及其他不足之处,还请各位同学多多提意见。


点此源码下载



7 0