自定義View繪制時(shí)鐘表盤

重要:原創(chuàng),轉(zhuǎn)載注明出處trueMi-簡(jiǎn)書

首先看下效果圖:

表盤時(shí)鐘演示.gif

實(shí)現(xiàn)步驟:

  • 繪制表盤[刻度,數(shù)字]
  • 繪制指針
  • 讓指針走起來(lái)~

具體如下:

繪制表盤:

首先需要計(jì)算出刻度的起點(diǎn)和終點(diǎn)坐標(biāo)值,這里我們通過(guò)構(gòu)建兩個(gè)半徑不同的同心圓,大圓半徑減小圓半徑,就可以得到一條刻度,只用改變角度,就可以獲取所有刻度:
刻度.png
    /**
     * 通過(guò)改變角度值,獲取不同角度方向的外圓一點(diǎn)到圓心連線過(guò)內(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)在于對(duì)指針旋轉(zhuǎn)的理解:
表盤旋轉(zhuǎn).png

通過(guò)上圖可以看到,我們通過(guò)旋轉(zhuǎn)畫布,然后繪制指針,最后恢復(fù)畫布,從而改變了指針的指向.
具體操作過(guò)程是:

  1. 保存已經(jīng)繪制畫面
  2. 以一定角度旋轉(zhuǎn)畫布
  3. 繪制指針
  4. 恢復(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í)間走起來(lái)

通過(guò)實(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; //最小寬/高的一半長(zhǎng)度
    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;
    }

    /**
     * 通過(guò)改變角度值,獲取不同角度方向的外圓一點(diǎn)到圓心連線過(guò)內(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); //左對(duì)齊
        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ì)的注釋,有問(wèn)題可以留言討論哦~~~

重要:轉(zhuǎn)載注明出處

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容