前段時(shí)間一直忙著學(xué)理財(cái)給自己充電鲤看,有一段時(shí)間沒寫代碼了缘揪,前兩天做了一個(gè)夢(mèng),夢(mèng)見自己在寫代碼义桂,是做了一個(gè)自定義時(shí)鐘的一個(gè)東西找筝,然后我醒來后就想著把這個(gè)效果實(shí)現(xiàn)以下,也算是“夢(mèng)”想成真了慷吊。先來看下效果圖吧~
OK袖裕,話不多說,直接安排~
1.初始化畫筆
定義好需要的畫筆溉瓶,這里我們先定義4個(gè)急鳄,分別是繪制背景谤民、基本元素、指針疾宏、數(shù)字四個(gè)Paint畫筆张足。然后初始化進(jìn)行畫筆的基本設(shè)置。
/**
* 背景繪制畫筆
*/
private Paint mPaintBg;
/**
* 時(shí)鐘元素畫筆
*/
private Paint mPaint;
/**
* 繪制指針的畫筆
*/
private Paint mPaintLine;
/**
* 繪制數(shù)字的畫筆
*/
private Paint mPaintText;
//------------------------------------------------------------------------------
/**
* 初始化
*/
private void initView() {
mPaintBg = new Paint();
mPaintBg.setAntiAlias(true);
mPaintBg.setColor(Color.BLACK);
mPaintBg.setStrokeWidth(dip2px(mContext, 2));
mPaintBg.setStyle(Paint.Style.STROKE);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaintLine = new Paint();
mPaintLine.setAntiAlias(true);
mPaintLine.setStyle(Paint.Style.FILL);
mPaintLine.setStrokeWidth(dip2px(mContext, 2));
mPaintText = new Paint();
mPaintText.setAntiAlias(true);
mPaintText.setColor(Color.BLACK);
mPaintText.setStyle(Paint.Style.STROKE);
mPaintText.setTextSize(30);
}
2.初始化變量
這里要定義三個(gè)變量來控制三個(gè)指針的長(zhǎng)度坎藐,也用于后面計(jì)算坐標(biāo)为牍。
/**
* 指針長(zhǎng)度-時(shí)針
*/
private float mDistanceHour;
/**
* 指針長(zhǎng)度-分針
*/
private float mDistanceMinute;
/**
* 指針長(zhǎng)度-秒針
*/
private float mDistanceSecond;
//------------------------------------------------------------------------------
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mDistanceHour = w / 5 - dip2px(mContext, 7);
mDistanceMinute = mDistanceHour + dip2px(mContext, 18);
mDistanceSecond = mDistanceHour + dip2px(mContext, 45);
}
3.定義相關(guān)變量
這里我對(duì)時(shí)分秒分別定義了變量進(jìn)行控制處理,主要是用來根據(jù)當(dāng)前時(shí)間來計(jì)算指針?biāo)枰獎(jiǎng)澾^的角度岩馍。
/**
* 0-11
*/
private int mCurrentHour = 0;
/**
* 0-59
*/
private int mCurrentMinute = 0;
/**
* 0-59
*/
private int mCurrentSecond = 0;
OK俱尼,接下來上核心代碼狸眼,也就是繪制部分劈愚。為了方便計(jì)算镜硕,我們把畫筆移動(dòng)到正中央,也就是先調(diào)用一下canvas.translate(getWidth() / 2, getHeight() / 2);然后再進(jìn)行繪制双谆。
4.繪制時(shí)鐘基本元素
/**
* 繪制時(shí)鐘基本的元素
*
* @param canvas
*/
private void drawClock(Canvas canvas) {
//繪制圓圈背景
canvas.drawCircle(0, 0, (getWidth() - dip2px(mContext, 2)) / 2, mPaintBg);
//繪制文字和小圓點(diǎn)
for (int i = 0; i < 12; i++) {
float r = getWidth() / 2 - offset;
float deg = i * 30 + 30;
float x = (float) (r * Math.sin(Math.PI * deg / 180));
float y = (float) (r * Math.cos(Math.PI * deg / 180));
canvas.drawCircle(x, -y, dip2px(mContext, 3), mPaint);
//繪制進(jìn)度數(shù)字
String text = (i + 1) + "";
//獲取文字寬度
float textWidth = mPaintText.measureText(text, 0, text.length());
float dx = x - x / 10 - textWidth / 2;
Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
float baseLine = -(y - y / 10) + dy;
canvas.drawText(text, dx, baseLine, mPaintText);
}
}
到這里我們就能看到基本的樣子已經(jīng)出來了壳咕,如下圖:
5.繪制時(shí)針
/**
* 繪制時(shí)針
*
* @param canvas
*/
private void drawHour(Canvas canvas) {
mPaintLine.setColor(Color.BLACK);
/**
* 找到時(shí)間和弧度的關(guān)系,先計(jì)算出總的時(shí)間加在一起是多少個(gè)小時(shí),
* 然后根據(jù)小時(shí)數(shù)計(jì)算出在時(shí)鐘上需要繪制的角度是多少佃乘,再求出坐標(biāo)進(jìn)行繪制
*/
float hour = (float) mCurrentHour + (float) mCurrentMinute / 60 + (float) mCurrentSecond / 3600;
float deg1 = hour * 30;
Log.i(TAG, "度數(shù)deg1:" + hour);
float x1 = (float) (mDistanceHour * Math.sin(Math.PI * deg1 / 180));
float y1 = (float) (mDistanceHour * Math.cos(Math.PI * deg1 / 180));
//canvas.drawCircle(x1, -y1, 10, mPaintLine);
canvas.drawLine(0, 0, x1, -y1, mPaintLine);
}
6.繪制分針
/**
* 繪制分針
*
* @param canvas
*/
private void drawMinute(Canvas canvas) {
mPaintLine.setColor(Color.BLACK);
/**
* 找到時(shí)間和弧度的關(guān)系
*/
float minute = (float) mCurrentMinute + (float) mCurrentSecond / 60;
float deg1 = minute * 6;
Log.i(TAG, "===度數(shù)minute:" + minute + "===度數(shù)deg1:" + deg1);
float x1 = (float) (mDistanceMinute * Math.sin(Math.PI * deg1 / 180));
float y1 = (float) (mDistanceMinute * Math.cos(Math.PI * deg1 / 180));
//canvas.drawCircle(x1, -y1, 10, mPaintLine);
canvas.drawLine(0, 0, x1, -y1, mPaintLine);
}
7.繪制秒針
/**
* 繪制秒針
*
* @param canvas
*/
private void drawSecond(Canvas canvas) {
mPaintLine.setColor(Color.RED);
/**
* 找到時(shí)間和弧度的關(guān)系
*/
float deg1 = mCurrentSecond * 6;
Log.i(TAG, "===度數(shù)deg1:" + deg1);
float x1 = (float) (mDistanceSecond * Math.sin(Math.PI * deg1 / 180));
float y1 = (float) (mDistanceSecond * Math.cos(Math.PI * deg1 / 180));
//canvas.drawCircle(x1, -y1, 10, mPaintLine);
canvas.drawLine(0, 0, x1, -y1, mPaintLine);
}
8.動(dòng)起來
如何動(dòng)起來囱井,就很簡(jiǎn)單了驹尼,我們只要每隔1秒進(jìn)行繪制一次即可趣避,這里我使用了Handler+Runnable的方式進(jìn)行實(shí)現(xiàn)的,代碼很簡(jiǎn)單就不單獨(dú)列出來了新翎,下面直接上完整代碼程帕。
ClockView.java
/**
* 自定義View系列-時(shí)鐘Clock
*/
public class ClockView extends View {
private static final String TAG = ClockView.class.getSimpleName();
/**
* 上下文
*/
private Context mContext;
/**
* 背景繪制畫筆
*/
private Paint mPaintBg;
/**
* 時(shí)鐘元素畫筆
*/
private Paint mPaint;
/**
* 繪制指針的畫筆
*/
private Paint mPaintLine;
/**
* 繪制數(shù)字的畫筆
*/
private Paint mPaintText;
/**
* 0-11
*/
private int mCurrentHour = 0;
/**
* 0-59
*/
private int mCurrentMinute = 0;
/**
* 0-59
*/
private int mCurrentSecond = 0;
/**
* 指針長(zhǎng)度-時(shí)針
*/
private float mDistanceHour;
/**
* 指針長(zhǎng)度-分針
*/
private float mDistanceMinute;
/**
* 指針長(zhǎng)度-秒針
*/
private float mDistanceSecond;
/**
* 偏移量
*/
private float offset;
/**
* 時(shí)間間隔
*/
private int TIME = 1000;//默認(rèn)每隔10ms重繪一次
/**
* Handler
*/
Handler handler = new Handler();
/**
* Runnable
*/
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
handler.postDelayed(this, TIME);
switch (mTypeModel) {
case CURRENT_MODEL:
getCurrentTime();
break;
case DEFAULT_MODEL:
getDefaultTime();
break;
}
invalidate();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
public ClockView(Context context) {
this(context, null);
}
public ClockView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initView();
offset = dip2px(mContext, 10);
}
/**
* 初始化
*/
private void initView() {
mPaintBg = new Paint();
mPaintBg.setAntiAlias(true);
mPaintBg.setColor(Color.BLACK);
mPaintBg.setStrokeWidth(dip2px(mContext, 2));
mPaintBg.setStyle(Paint.Style.STROKE);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
mPaintLine = new Paint();
mPaintLine.setAntiAlias(true);
mPaintLine.setStyle(Paint.Style.FILL);
mPaintLine.setStrokeWidth(dip2px(mContext, 2));
mPaintText = new Paint();
mPaintText.setAntiAlias(true);
mPaintText.setColor(Color.BLACK);
mPaintText.setStyle(Paint.Style.STROKE);
mPaintText.setTextSize(30);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mDistanceHour = w / 5 - dip2px(mContext, 7);
mDistanceMinute = mDistanceHour + dip2px(mContext, 18);
mDistanceSecond = mDistanceHour + dip2px(mContext, 45);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth() / 2, getHeight() / 2);
drawClock(canvas);
drawHour(canvas);
drawMinute(canvas);
drawSecond(canvas);
/**
* 繪制中心點(diǎn)
*/
canvas.drawCircle(0, 0, 20, mPaint);
}
/**
* 繪制時(shí)鐘基本的元素
*
* @param canvas
*/
private void drawClock(Canvas canvas) {
//繪制圓圈背景
canvas.drawCircle(0, 0, (getWidth() - dip2px(mContext, 2)) / 2, mPaintBg);
//繪制文字和小圓點(diǎn)
for (int i = 0; i < 12; i++) {
float r = getWidth() / 2 - offset;
float deg = i * 30 + 30;
float x = (float) (r * Math.sin(Math.PI * deg / 180));
float y = (float) (r * Math.cos(Math.PI * deg / 180));
canvas.drawCircle(x, -y, dip2px(mContext, 3), mPaint);
//繪制進(jìn)度數(shù)字
String text = (i + 1) + "";
//獲取文字寬度
float textWidth = mPaintText.measureText(text, 0, text.length());
float dx = x - x / 10 - textWidth / 2;
Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
float dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
float baseLine = -(y - y / 10) + dy;
canvas.drawText(text, dx, baseLine, mPaintText);
}
}
/**
* 繪制時(shí)針
*
* @param canvas
*/
private void drawHour(Canvas canvas) {
mPaintLine.setColor(Color.BLACK);
/**
* 找到時(shí)間和弧度的關(guān)系,先計(jì)算出總的時(shí)間加在一起是多少個(gè)小時(shí),
* 然后根據(jù)小時(shí)數(shù)計(jì)算出在時(shí)鐘上需要繪制的角度是多少地啰,再求出坐標(biāo)進(jìn)行繪制
*/
float hour = (float) mCurrentHour + (float) mCurrentMinute / 60 + (float) mCurrentSecond / 3600;
float deg1 = hour * 30;
Log.i(TAG, "度數(shù)deg1:" + hour);
float x1 = (float) (mDistanceHour * Math.sin(Math.PI * deg1 / 180));
float y1 = (float) (mDistanceHour * Math.cos(Math.PI * deg1 / 180));
//canvas.drawCircle(x1, -y1, 10, mPaintLine);
canvas.drawLine(0, 0, x1, -y1, mPaintLine);
}
/**
* 繪制分針
*
* @param canvas
*/
private void drawMinute(Canvas canvas) {
mPaintLine.setColor(Color.BLACK);
/**
* 找到時(shí)間和弧度的關(guān)系
*/
float minute = (float) mCurrentMinute + (float) mCurrentSecond / 60;
float deg1 = minute * 6;
Log.i(TAG, "===度數(shù)minute:" + minute + "===度數(shù)deg1:" + deg1);
float x1 = (float) (mDistanceMinute * Math.sin(Math.PI * deg1 / 180));
float y1 = (float) (mDistanceMinute * Math.cos(Math.PI * deg1 / 180));
//canvas.drawCircle(x1, -y1, 10, mPaintLine);
canvas.drawLine(0, 0, x1, -y1, mPaintLine);
}
/**
* 繪制秒針
*
* @param canvas
*/
private void drawSecond(Canvas canvas) {
mPaintLine.setColor(Color.RED);
/**
* 找到時(shí)間和弧度的關(guān)系
*/
float deg1 = mCurrentSecond * 6;
Log.i(TAG, "===度數(shù)deg1:" + deg1);
float x1 = (float) (mDistanceSecond * Math.sin(Math.PI * deg1 / 180));
float y1 = (float) (mDistanceSecond * Math.cos(Math.PI * deg1 / 180));
//canvas.drawCircle(x1, -y1, 10, mPaintLine);
canvas.drawLine(0, 0, x1, -y1, mPaintLine);
}
/**
* 開始
*/
public void start() {
stop();
handler.postDelayed(runnable, TIME); //每隔TIMEms執(zhí)行
}
public void stop() {
try {
handler.removeCallbacks(runnable);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取當(dāng)前時(shí)間
*/
private void getCurrentTime() {
Calendar now = Calendar.getInstance();
mCurrentHour = now.get(Calendar.HOUR_OF_DAY);
mCurrentMinute = now.get(Calendar.MINUTE);
mCurrentSecond = now.get(Calendar.SECOND);
}
private long timeSeconds = 32353450;
private void getDefaultTime() {
timeSeconds++;
mCurrentHour = (int) (timeSeconds / 60 / 60 % 60);
mCurrentMinute = (int) (timeSeconds / 60 % 60);
mCurrentSecond = (int) (timeSeconds % 60);
}
private TYPE_MODEL mTypeModel = TYPE_MODEL.DEFAULT_MODEL;
public enum TYPE_MODEL {
CURRENT_MODEL, DEFAULT_MODEL;
}
public void startSpeed() {
mTypeModel = TYPE_MODEL.DEFAULT_MODEL;
TIME = 1;
}
public void defaultModel() {
TIME = 1000;
mTypeModel = TYPE_MODEL.CURRENT_MODEL;
}
/**
* 根據(jù)手機(jī)的分辨率從 dp 的單位 轉(zhuǎn)成為 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ClockView mClockView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mClockView = findViewById(R.id.view);
mClockView.start();
}
/**
* 加快版
*
* @param view
*/
public void clickSpeed(View view) {
mClockView.startSpeed();
}
/**
* 當(dāng)前時(shí)間
*
* @param view
*/
public void clickDefault(View view) {
mClockView.defaultModel();
}
}
OK愁拭,結(jié)束,打完收工亏吝!