Android實(shí)現(xiàn)簡單時(shí)鐘View的方法
通過Canvas的平移與旋轉(zhuǎn)簡化繪圖邏輯是一個(gè)非常有用的技巧,下面的時(shí)鐘view就是利用這個(gè)方法完成的,省去了使用三角函數(shù)計(jì)算坐標(biāo)的麻煩。

package com.example.swt369.simpleclock;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Toast;
import java.util.Calendar;
/**
* Created by swt369 on 2017/8/20.
*/
public class ClockView extends View {
//時(shí)鐘各個(gè)部件的長度
private float mScaleLengthLong;
private float mScaleLengthShort;
private float mTickLengthHour;
private float mTickLengthMinute;
private float mTickLengthSecond;
//view的寬和高
private float mWidth;
private float mHeight;
//時(shí)鐘半徑
private float mRadius;
//繪制各部件時(shí)用的Paint
private Paint mPaintScaleLong;
private Paint mPaintScaleShort;
private Paint mPaintOutline;
private Paint mPaintNum;
private Paint mPaintTickHour;
private Paint mPaintTickMinute;
private Paint mPaintTickSecond;
private ViewTreeObserver.OnPreDrawListener onPreDrawListener;
public ClockView(final Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
//獲取view寬高并計(jì)算各個(gè)部件的長度
@Override
public boolean onPreDraw() {
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
mRadius = Math.min(mWidth,mHeight) / 2 * 0.95f;
mScaleLengthLong = mRadius * 0.1f;
mScaleLengthShort = mRadius * 0.05f;
mTickLengthHour = mRadius * 0.3f;
mTickLengthMinute = mRadius * 0.45f;
mTickLengthSecond = mRadius * 0.6f;
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(onPreDrawListener);
//點(diǎn)擊顯示具體時(shí)間
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Calendar calendar = Calendar.getInstance();
String time = String.format("當(dāng)前時(shí)間:%02d:%02d:%02d",
calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND));
Toast.makeText(context,time, Toast.LENGTH_SHORT).show();
}
});
//初始化所有Paint對象
initializePaints();
}
@Override
protected void onDraw(Canvas canvas) {
if(mWidth == 0 || mHeight == 0){
return;
}
if(onPreDrawListener != null){
getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener);
onPreDrawListener = null;
}
//繪制時(shí)鐘
drawClock(canvas);
//一秒后重繪
postInvalidateDelayed(1000);
}
private void drawClock(Canvas canvas) {
//保存原始狀態(tài)
canvas.save();
//將坐標(biāo)系原點(diǎn)移到中心,并逆時(shí)針旋轉(zhuǎn)90度。完成后x軸朝上。
canvas.translate(mWidth / 2,mHeight / 2);
canvas.rotate(-90);
//畫外圍輪廓
canvas.drawCircle(0,0,mRadius, mPaintOutline);
//畫刻度
for(int i = 0 ; i < 12 ; i++){
String num = String.valueOf(i == 0 ? 12 : i);
if(i % 3 == 0){
canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong);
}else {
canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort);
}
canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum);
//順時(shí)針旋轉(zhuǎn)30度
canvas.rotate(30);
}
Calendar calendar = Calendar.getInstance();
//畫時(shí)針
int hour = calendar.get(Calendar.HOUR);
canvas.save();
canvas.rotate(hour * 30);
canvas.drawLine(0,0,mTickLengthHour,0,mPaintTickHour);
canvas.restore();
//畫分針
int minute = calendar.get(Calendar.MINUTE);
canvas.save();
canvas.rotate(minute * 6);
canvas.drawLine(0,0,mTickLengthMinute,0,mPaintTickMinute);
canvas.restore();
//畫秒針
int second = calendar.get(Calendar.SECOND);
canvas.save();
canvas.rotate(second * 6);
canvas.drawLine(0,0,mTickLengthSecond,0,mPaintTickSecond);
canvas.restore();
//恢復(fù)原始狀態(tài)
canvas.restore();
}
private void initializePaints(){
mPaintScaleLong = new Paint();
mPaintScaleLong.setAntiAlias(true);
mPaintScaleLong.setStrokeWidth(5);
mPaintScaleShort = new Paint();
mPaintScaleShort.setAntiAlias(true);
mPaintScaleShort.setStrokeWidth(3);
mPaintOutline = new Paint();
mPaintOutline.setStyle(Paint.Style.STROKE);
mPaintOutline.setAntiAlias(true);
mPaintOutline.setStrokeWidth(5);
mPaintNum = new Paint();
mPaintNum.setTextSize(30);
mPaintTickHour = new Paint();
mPaintTickHour.setAntiAlias(true);
mPaintTickHour.setStrokeWidth(6);
mPaintTickMinute = new Paint();
mPaintTickMinute.setAntiAlias(true);
mPaintTickMinute.setStrokeWidth(4);
mPaintTickSecond = new Paint();
mPaintTickSecond.setAntiAlias(true);
mPaintTickSecond.setStrokeWidth(2);
}
}
代碼注釋已經(jīng)比較詳細(xì)了,下面把比較重要的部分再說明一下:
//保存原始狀態(tài) canvas.save(); //省略中間部分... //恢復(fù)原始狀態(tài) canvas.restore();
在繪圖開始時(shí)調(diào)用canvas.save(),可以保存下未經(jīng)任何平移、旋轉(zhuǎn)操作的原始畫布狀態(tài)。在所有繪圖工作完成后調(diào)用canvas.restore(),可以恢復(fù)到上一次保存的狀態(tài)(類似進(jìn)出棧的感覺)。
//將坐標(biāo)系原點(diǎn)移到中心,并逆時(shí)針旋轉(zhuǎn)90度。完成后x軸朝上。 canvas.translate(mWidth / 2,mHeight / 2); canvas.rotate(-90);
畫布的平移與旋轉(zhuǎn)可能比較抽象,可以想象成坐標(biāo)系的平移與旋轉(zhuǎn)。調(diào)用canvas.translate(dx,dy)相當(dāng)于將坐標(biāo)原點(diǎn)向x,y方向移動(dòng)了dx,dy的距離,調(diào)用canvas.rotate(degree)相當(dāng)于坐標(biāo)系順時(shí)針旋轉(zhuǎn)了degree°。
//畫刻度
for(int i = 0 ; i < 12 ; i++){
String num = String.valueOf(i == 0 ? 12 : i);
if(i % 3 == 0){
canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong);
}else {
canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort);
}
canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum);
//順時(shí)針旋轉(zhuǎn)30度
canvas.rotate(30);
}
根據(jù)i值決定是畫大刻度(90°的倍數(shù))還是小刻度。每次畫完一條之后將坐標(biāo)系順時(shí)針旋轉(zhuǎn)30°,這樣保證每次畫的刻度線的坐標(biāo)不變。
//一秒后重繪 postInvalidateDelayed(1000);
作用是在1000ms(1s)后再次執(zhí)行View的draw流程,產(chǎn)生指針運(yùn)動(dòng)的效果。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android獲取設(shè)備CPU核數(shù)、時(shí)鐘頻率以及內(nèi)存大小的方法
- Android多功能時(shí)鐘開發(fā)案例(實(shí)戰(zhàn)篇)
- android實(shí)現(xiàn)widget時(shí)鐘示例分享
- Android 仿日歷翻頁、仿htc時(shí)鐘翻頁、數(shù)字翻頁切換效果
- Android多功能時(shí)鐘開發(fā)案例(基礎(chǔ)篇)
- android高仿小米時(shí)鐘(使用Camera和Matrix實(shí)現(xiàn)3D效果)
- Android自定義動(dòng)態(tài)壁紙開發(fā)(時(shí)鐘)
- Android編程基于自定義控件實(shí)現(xiàn)時(shí)鐘功能的方法
- Android仿小米時(shí)鐘效果
- Android自定義View實(shí)現(xiàn)時(shí)鐘功能
相關(guān)文章
Android實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤實(shí)例代碼,可以應(yīng)用于Android游戲開發(fā)中的一個(gè)應(yīng)用,需要的朋友可以參考下2014-07-07
android中px和dp,px和sp之間的轉(zhuǎn)換方法
在Android開發(fā)中dp和px,sp和px之間的轉(zhuǎn)換時(shí)必不可少的。下面腳本之家小編給大家?guī)砹薬ndroid中px和dp,px和sp之間的轉(zhuǎn)換方法,感興趣的朋友一起看看吧2018-06-06
Android實(shí)現(xiàn)計(jì)算器(計(jì)算表達(dá)式/計(jì)算小數(shù)點(diǎn)以及括號)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)計(jì)算器功能,計(jì)算表達(dá)式,能計(jì)算小數(shù)點(diǎn)以及括號,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09
Flutter路由跳轉(zhuǎn)參數(shù)處理技巧詳解
這篇文章主要為大家介紹了Flutter路由跳轉(zhuǎn)參數(shù)處理技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
Android Scroll滑動(dòng)效果實(shí)例
這篇文章主要為大家分享了Android Scroll滑動(dòng)效果實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04
詳解Android .9.png “點(diǎn)九”圖片的使用
這篇文章主要為大家詳細(xì)介紹了Android .9.png “點(diǎn)九”圖片的使用方法,感興趣的小伙伴們可以參考一下2016-09-09

