重要:原創(chuàng),轉(zhuǎn)載注明出處trueMi-簡書
首先看下效果圖:
實(shí)現(xiàn)步驟:
- 繪制表盤[刻度,數(shù)字]
- 繪制指針
- 讓指針走起來~
具體如下:
繪制表盤:
首先需要計(jì)算出刻度的起點(diǎn)和終點(diǎn)坐標(biāo)值,這里我們通過構(gòu)建兩個(gè)半徑不同的同心圓,大圓半徑減小圓半徑,就可以得到一條刻度,只用改變角度,就可以獲取所有刻度: /**
* 通過改變角度值,獲取不同角度方向的外圓一點(diǎn)到圓心連線過內(nèi)圓一點(diǎn)的路徑坐標(biāo)集合
* @param x0 圓心x
* @param y0 圓心y
* @param outRadius 外圓半徑
* @param innerRadius 內(nèi)圓半徑
* @param angle 角度
* @return 返回
*/
private float[] getDialPaths(int x0,int y0,int outRadius,int innerRadius,int angle){
float[] paths = new float[4];
paths[0] = (float) (x0 + outRadius * Math.cos(angle * Math.PI / 180));
paths[1] = (float) (y0 + outRadius * Math.sin(angle * Math.PI / 180));
paths[2] = (float) (x0 + innerRadius * Math.cos(angle * Math.PI / 180));
paths[3] = (float) (y0 + innerRadius * Math.sin(angle * Math.PI / 180));
return paths;
}
秒針刻度間隔360/60 = 6 度,循環(huán)繪制60次,每一次角度加6,就可以了;繪制代碼如下:
for (int i = 0; i < 60 ; i++) {
if (i % 5 == 0){
//獲取刻度路徑
float[] dialKdPaths = getDialPaths(halfMinLength, halfMinLength, halfMinLength - 8, halfMinLength * 5 / 6, -i * 6);
canvas.drawLines(dialKdPaths,paintKd30);
float[] dialPathsStr = getDialPaths(halfMinLength, halfMinLength, halfMinLength - 8, halfMinLength * 3 / 4, -i * 6);
canvas.drawText(strKedu[i/5],dialPathsStr[2] - 16,dialPathsStr[3] + 14,paintKd30Text);
continue;
}
float[] dialKdPaths = getDialPaths(halfMinLength, halfMinLength, halfMinLength - 8, halfMinLength * 7 / 8, -i * 6);
canvas.drawLines(dialKdPaths,paintKdSecond);
}
繪制指針和旋轉(zhuǎn)指針
這里的重點(diǎn)在于對指針旋轉(zhuǎn)的理解:通過上圖可以看到,我們通過旋轉(zhuǎn)畫布,然后繪制指針,最后恢復(fù)畫布,從而改變了指針的指向.
具體操作過程是:
- 保存已經(jīng)繪制畫面
- 以一定角度旋轉(zhuǎn)畫布
- 繪制指針
- 恢復(fù)畫布角度
代碼如下:以時(shí)針繪制為例
//時(shí)針繪制
canvas.save(); //保存之前內(nèi)容
canvas.rotate(angleHour,halfMinLength,halfMinLength); //旋轉(zhuǎn)的是畫布,從而得到指針旋轉(zhuǎn)的效果
canvas.drawLine(halfMinLength,halfMinLength,halfMinLength,halfMinLength*3/4,paintHour);
canvas.restore(); //恢復(fù)
讓時(shí)間走起來
通過實(shí)時(shí)的計(jì)算時(shí)針,分針,秒針的角度,然后通知重新繪制畫面,我們就看到時(shí)間在走動(dòng).
/**
* 更新時(shí)分秒針的角度,開始繪制
*/
public void startRun(){
new Thread(new Runnable() {
@Override
public void run() {
while (drawable){
try {
Thread.sleep(1000); // 睡1s
updataAngleSecond(); //更新秒針角度
updataAngleMinute(); //更新分針角度
updataAngleHour(); //更新時(shí)針角度
postInvalidate(); //重新繪制
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
完整代碼如下:
package com.truemi.dialapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import java.util.Calendar;
public class DialView extends View {
private boolean drawable = true; //是否可以繪制
private int halfMinLength; //最小寬/高的一半長度
private Paint paintKd30; //時(shí)針刻度線畫筆
private Paint paintKd30Text; // 時(shí)針數(shù)字畫筆
private Paint paintKdSecond; //秒針刻度線畫筆
private Paint paintHour; //時(shí)針畫筆
private Paint paintCircleBar;//指針圓心畫筆
private Paint paintMinute; //分針畫筆
private Paint paintSecond; //秒針畫筆
private float angleHour; //時(shí)針旋轉(zhuǎn)角度
private float angleMinute; //分針旋轉(zhuǎn)角度
private float angleSecond; //秒針旋轉(zhuǎn)角度
private int cuurSecond; //當(dāng)前秒
private int cuurMinute; //當(dāng)前分
private int cuurHour; //當(dāng)前時(shí)
private Calendar mCalendar;
private boolean isMorning = true; //上午/下午
private String[] strKedu = {"3","2","1","12","11","10","9","8","7","6","5","4"};
public DialView(Context context) {
this(context,null);
}
public DialView(Context context, AttributeSet attrs) {
this(context, attrs,-1);
}
public DialView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint(); //初始化畫筆
initTime(); //初始化時(shí)間
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
halfMinLength = Math.min(width,height) / 2;
System.out.println(halfMinLength);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//表盤刻度繪制
for (int i = 0; i < 60 ; i++) {
if (i % 5 == 0){
//獲取刻度路徑
float[] dialKdPaths = getDialPaths(halfMinLength, halfMinLength, halfMinLength - 8, halfMinLength * 5 / 6, -i * 6);
canvas.drawLines(dialKdPaths,paintKd30);
float[] dialPathsStr = getDialPaths(halfMinLength, halfMinLength, halfMinLength - 8, halfMinLength * 3 / 4, -i * 6);
canvas.drawText(strKedu[i/5],dialPathsStr[2] - 16,dialPathsStr[3] + 14,paintKd30Text);
continue;
}
float[] dialKdPaths = getDialPaths(halfMinLength, halfMinLength, halfMinLength - 8, halfMinLength * 7 / 8, -i * 6);
canvas.drawLines(dialKdPaths,paintKdSecond);
}
//指針繪制
//時(shí)針繪制
canvas.save(); //保存之前內(nèi)容
canvas.rotate(angleHour,halfMinLength,halfMinLength); //旋轉(zhuǎn)的是畫布,從而得到指針旋轉(zhuǎn)的效果
canvas.drawLine(halfMinLength,halfMinLength,halfMinLength,halfMinLength*3/4,paintHour);
canvas.restore(); //恢復(fù)
//繪制分針
canvas.save();
canvas.rotate(angleMinute,halfMinLength,halfMinLength); //旋轉(zhuǎn)的是畫布,從而得到指針旋轉(zhuǎn)的效果
canvas.drawLine(halfMinLength,halfMinLength,halfMinLength,halfMinLength/2,paintMinute);
paintCircleBar.setColor(Color.rgb(75,75,75));
paintCircleBar.setShadowLayer(4,4,8,Color.argb(70,40,40,40));
canvas.drawCircle(halfMinLength,halfMinLength,24,paintCircleBar);
canvas.restore();
//繪制秒針
canvas.save();
canvas.rotate(angleSecond,halfMinLength,halfMinLength); //旋轉(zhuǎn)的是畫布,從而得到指針旋轉(zhuǎn)的效果
canvas.drawLine(halfMinLength,halfMinLength + 40,halfMinLength,halfMinLength / 4 - 20,paintSecond);
paintCircleBar.setColor(Color.rgb(178,34,34));
paintCircleBar.setShadowLayer(4,4,8,Color.argb(50,80,0,0));
canvas.drawCircle(halfMinLength,halfMinLength,12,paintCircleBar);
canvas.restore();
}
/**
* 初始化時(shí),分,秒
*/
private void initTime() {
mCalendar = Calendar.getInstance();
cuurHour = mCalendar.get(Calendar.HOUR_OF_DAY);
cuurMinute = mCalendar.get(Calendar.MINUTE);
cuurSecond = mCalendar.get(Calendar.SECOND);
if (cuurHour >= 12){
cuurHour = cuurHour - 12;
isMorning = false;
}else{
isMorning = true;
}
angleSecond = cuurSecond * 6f;
angleMinute = cuurMinute * 6f;
angleHour = cuurHour * 6f * 5f;
}
/**
* 更新時(shí)分秒針的角度,開始繪制
*/
public void startRun(){
new Thread(new Runnable() {
@Override
public void run() {
while (drawable){
try {
Thread.sleep(1000); // 睡1s
updataAngleSecond(); //更新秒針角度
updataAngleMinute(); //更新分針角度
updataAngleHour(); //更新時(shí)針角度
postInvalidate(); //重新繪制
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
private void updataAngleHour() {
//更新時(shí)針角度
angleHour = angleHour + (30f/3600);
if (angleHour >= 360){
angleHour = 0;
cuurHour = 0;
}
}
private void updataAngleMinute() {
//更新分針角度
angleMinute = angleMinute + 0.1f;
if (angleMinute >= 360){
angleMinute = 0;
cuurMinute = 0;
cuurHour += 1;
}
}
private void updataAngleSecond() {
//更新秒針角度
angleSecond = angleSecond + 6;
cuurSecond += 1;
if (angleSecond >= 360){
angleSecond = 0;
cuurSecond = 0;
cuurMinute += 1;
//一分鐘同步一次本地時(shí)間
mCalendar = Calendar.getInstance();
cuurHour = mCalendar.get(Calendar.HOUR_OF_DAY);
cuurMinute = mCalendar.get(Calendar.MINUTE);
cuurSecond = mCalendar.get(Calendar.SECOND);
if (cuurHour >= 12){
cuurHour = cuurHour - 12;
isMorning = false;
}else{
isMorning = true;
}
angleSecond = cuurSecond * 6f;
angleMinute = cuurMinute * 6f;
angleHour = cuurHour * 6f * 5f;
}
}
/**
* 停止繪制
*/
public void stopDrawing(){
drawable = false;
}
/**
* 通過改變角度值,獲取不同角度方向的外圓一點(diǎn)到圓心連線過內(nèi)圓一點(diǎn)的路徑坐標(biāo)集合
* @param x0 圓心x
* @param y0 圓心y
* @param outRadius 外圓半徑
* @param innerRadius 內(nèi)圓半徑
* @param angle 角度
* @return 返回
*/
private float[] getDialPaths(int x0,int y0,int outRadius,int innerRadius,int angle){
float[] paths = new float[4];
paths[0] = (float) (x0 + outRadius * Math.cos(angle * Math.PI / 180));
paths[1] = (float) (y0 + outRadius * Math.sin(angle * Math.PI / 180));
paths[2] = (float) (x0 + innerRadius * Math.cos(angle * Math.PI / 180));
paths[3] = (float) (y0 + innerRadius * Math.sin(angle * Math.PI / 180));
return paths;
}
/**
* 初始化畫筆參數(shù)
*/
private void initPaint() {
paintKd30 = new Paint();
paintKd30.setStrokeWidth(8);
paintKd30.setColor(Color.rgb(75,75,75));
paintKd30.setAntiAlias(true);
paintKd30.setDither(true);
paintKd30.setStrokeCap(Paint.Cap.ROUND);
paintKd30Text = new Paint();
paintKd30Text.setTextAlign(Paint.Align.LEFT); //左對齊
paintKd30Text.setStrokeWidth(6); //設(shè)置寬度
paintKd30Text.setTextSize(40); //文字大小
paintKd30Text.setTypeface(Typeface.DEFAULT_BOLD); //加粗
paintKd30Text.setColor(Color.rgb(75,75,75)); //畫筆顏色
paintKd30Text.setAntiAlias(true); //抗鋸齒
paintKd30Text.setDither(true); //抖動(dòng)
paintKd30Text.setStrokeCap(Paint.Cap.ROUND); //筆尖圓角
paintKd30Text.setShadowLayer(4,2,4,Color.argb(60,90,90,90)); //陰影
paintKdSecond = new Paint();
paintKdSecond.setStrokeWidth(6);
paintKdSecond.setColor(Color.rgb(75,75,75));
paintKdSecond.setAntiAlias(true);
paintKdSecond.setDither(true);
paintKdSecond.setStrokeCap(Paint.Cap.ROUND);
paintKdSecond.setShadowLayer(4,5,10,Color.argb(50,80,80,80));
paintHour = new Paint();
paintHour.setStrokeWidth(30);
paintHour.setColor(Color.rgb(75,75,75));
paintHour.setAntiAlias(true);
paintHour.setDither(true);
paintHour.setStrokeCap(Paint.Cap.ROUND);
paintHour.setShadowLayer(4,5,10,Color.argb(50,80,80,80));
paintCircleBar = new Paint();
paintCircleBar.setStrokeWidth(6);
// paintCircleBar.setColor(Color.rgb(178,34,34));
paintCircleBar.setAntiAlias(true);
paintCircleBar.setDither(true);
paintCircleBar.setStrokeCap(Paint.Cap.ROUND);
// paintCircleBar.setShadowLayer(4,5,10,Color.argb(100,80,80,80));
paintMinute = new Paint();
paintMinute.setStrokeWidth(30);
paintMinute.setColor(Color.rgb(75,75,75));
paintMinute.setAntiAlias(true);
paintMinute.setDither(true);
paintMinute.setStrokeCap(Paint.Cap.ROUND);
paintMinute.setShadowLayer(4,5,10,Color.rgb(80,80,80));
paintSecond = new Paint();
paintSecond.setStrokeWidth(6);
paintSecond.setColor(Color.rgb(180,30,30));
paintSecond.setAntiAlias(true);
paintSecond.setDither(true);
paintSecond.setStrokeCap(Paint.Cap.ROUND);
paintSecond.setShadowLayer(4,2,10,Color.argb(100,90,90,90));
}
}
代碼中有比較詳細(xì)的注釋,有問題可以留言討論哦~~~