Android 自定义View 实现表盘效果

看一哥们做了这个效果,我也来凑下热闹




我的实现:

  1. package com.stone.clock.view;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Canvas;  
  5. import android.graphics.Color;  
  6. import android.graphics.Paint;  
  7. import android.graphics.Path;  
  8. import android.graphics.RectF;  
  9. import android.os.Build;  
  10. import android.os.Handler;  
  11. import android.os.Message;  
  12. import android.os.SystemClock;  
  13. import android.support.annotation.RequiresApi;  
  14. import android.util.AttributeSet;  
  15. import android.view.View;  
  16.   
  17. import java.util.Calendar;  
  18. import java.util.TimeZone;  
  19.   
  20. /** 
  21.  * 时钟 
  22.  * author : stone 
  23.  * email  : aa86799@163.com 
  24.  * time   : 2016/11/7 17 46 
  25.  */  
  26.   
  27. public class ClockView extends View {  
  28.     private static final int H_POINTER_WIDTH = 15//时针宽  
  29.     private static final int M_POINTER_WIDTH = 10//分针宽  
  30.     private static final int S_POINTER_WIDTH = 5//秒针宽  
  31.     private static final int SCALE_LINE_LENGTH = 50//刻度线长  
  32.     private static final int SCALE_LINE_WIDTH = 6//刻度线宽  
  33.     private static final int M_S_DEGREES_UNIT = 360 / 60//分、秒针每个数字走的角度  
  34.     private static final int H_DEGREES_UNIT = 360 / 12//时针每个数字走的角度  
  35.     private Paint mPaint;  
  36.     private int mW, mH, mCx, mCy, mR;  
  37.     private int mCount;  
  38.   
  39.     private Handler mHandler = new Handler() {  
  40.         @Override  
  41.         public void handleMessage(Message msg) {  
  42.             super.handleMessage(msg);  
  43.             postInvalidate();  
  44.         }  
  45.     };  
  46.   
  47.     public ClockView(Context context) {  
  48.         this(context, null);  
  49.     }  
  50.   
  51.     public ClockView(Context context, AttributeSet attrs) {  
  52.         this(context, attrs, 0);  
  53.     }  
  54.   
  55.     public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {  
  56.         super(context, attrs, defStyleAttr);  
  57.         init();  
  58.     }  
  59.   
  60.     @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)  
  61.     public ClockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {  
  62.         super(context, attrs, defStyleAttr, defStyleRes);  
  63.         init();  
  64.     }  
  65.   
  66.     private void init() {  
  67.         mPaint = new Paint();  
  68.   
  69.     }  
  70.   
  71.     @Override  
  72.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  73.         super.onSizeChanged(w, h, oldw, oldh);  
  74.         mW = getWidth();  
  75.         mH = getHeight();  
  76.   
  77.         mR = Math.min(mW, mH) / 2 - 20;  
  78.         mCx = mW / 2;  
  79.         mCy = mH / 2;  
  80.   
  81.     }  
  82.   
  83.     @Override  
  84.     protected void onDraw(Canvas canvas) {  
  85.         super.onDraw(canvas);  
  86.   
  87.         long beginTime = SystemClock.uptimeMillis();  
  88.   
  89.         mPaint.setColor(Color.WHITE);  
  90.         canvas.drawCircle(mCx, mCy, mR, mPaint); //外圆  
  91.         mPaint.setColor(Color.BLACK);  
  92.         canvas.drawCircle(mCx, mCy, mR / 12, mPaint); //圆心  
  93.   
  94.         /* 
  95.         绘制刻度 
  96.          */  
  97.         for (int i = 0; i < 60; i++) {  
  98.             canvas.save();  
  99.             canvas.rotate(M_S_DEGREES_UNIT * i, mCx, mCy);  
  100.             mPaint.setStrokeWidth(SCALE_LINE_WIDTH);  
  101.             canvas.drawLine(mCx, mCy - mR, mCx, mCy - mR + SCALE_LINE_LENGTH, mPaint);  
  102.             if (i % 5 == 0) {  
  103.                 mPaint.setStrokeWidth(SCALE_LINE_WIDTH + 5);  
  104.                 canvas.drawLine(mCx, mCy - mR, mCx, mCy - mR + SCALE_LINE_LENGTH + 10, mPaint);  
  105.   
  106.                 String num = i == 0 ? 12 + "" : i / 5 + "";  
  107.                 float x = mCx - mPaint.measureText(num) / 2;  
  108.                 float y = mCy - mR + SCALE_LINE_LENGTH + 30;  
  109.                 mPaint.setTextSize(50);  
  110.                 canvas.drawText(num, x, y + Math.abs(mPaint.ascent()), mPaint);  
  111.             }  
  112.             canvas.restore();  
  113.   
  114.         }  
  115.   
  116.         Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));  
  117.         int hour = calendar.get(Calendar.HOUR);  
  118.         int minute = calendar.get(Calendar.MINUTE);  
  119.         int second = calendar.get(Calendar.SECOND);  
  120.   
  121.         /* 
  122.         分针在最下面 最先绘制 
  123.          */  
  124.         canvas.save();  
  125.         float mDegrees = minute * M_S_DEGREES_UNIT;  
  126.         canvas.rotate(mDegrees, mCx, mCy);  
  127.         mPaint.setColor(Color.GREEN);  
  128.         Path p = new Path();  
  129.         p.addRect(new RectF(mCx - M_POINTER_WIDTH, mCy - mR/4*3, mCx + M_POINTER_WIDTH, mCy + mR/5), Path.Direction.CW);  
  130.         p.quadTo(mCx, mCy - mR/4*3 - 40, mCx + M_POINTER_WIDTH, mCy - mR/4*3);  
  131.         canvas.drawPath(p, mPaint);  
  132.         canvas.restore();  
  133.   
  134.         /* 
  135.         时针在中间 
  136.             分针60分走360度; 时针1小时(即60分)走30度 
  137.             mDegrees:hDegrees = 360:30 = 12:1 
  138.             hDegrees = mDegrees / 12; 时针相对于分钟数所要走的角度 
  139.          */  
  140.         canvas.save();  
  141.         canvas.rotate(mDegrees / 12 + hour * H_DEGREES_UNIT, mCx, mCy);  
  142.         mPaint.setColor(Color.RED);  
  143.         p = new Path();  
  144.         p.addRect(new RectF(mCx - H_POINTER_WIDTH, mCy - mR/3*2, mCx + H_POINTER_WIDTH, mCy + mR/5), Path.Direction.CW);  
  145.         p.quadTo(mCx, mCy - mR/3*2 - 30, mCx + H_POINTER_WIDTH, mCy - mR/3*2);  
  146.         canvas.drawPath(p, mPaint);  
  147.         canvas.restore();  
  148.   
  149.         /* 
  150.         秒针在最上面 
  151.          */  
  152.         canvas.save();  
  153.         canvas.rotate(second * M_S_DEGREES_UNIT, mCx, mCy);  
  154.         mPaint.setColor(Color.BLACK);  
  155.         p = new Path();  
  156.         p.addRect(new RectF(mCx - S_POINTER_WIDTH, mCy - mR/5*4, mCx + S_POINTER_WIDTH, mCy + mR/5), Path.Direction.CW);  
  157.         p.quadTo(mCx, mCy - mR/5*4 - 30, mCx + S_POINTER_WIDTH, mCy - mR/5*4);  
  158.         canvas.drawPath(p, mPaint);  
  159.         canvas.restore();  
  160.   
  161.         long spanTime = SystemClock.uptimeMillis() - beginTime;  
  162. //        System.out.println("间隔时间" + spanTime);  
  163.         if (mCount < 5) {//12的位置开始绘制时位置不对,刷新一次后就正常,这里让其快速刷新几次  
  164.             mHandler.sendEmptyMessage(0);  
  165.             mCount++;  
  166.         } else  
  167.             mHandler.sendEmptyMessageDelayed(01000 - spanTime);  
  168.     }  
  169.   
  170. }  
package com.stone.clock.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;

import java.util.Calendar;
import java.util.TimeZone;

/**
 * 时钟
 * author : stone
 * email  : aa86799@163.com
 * time   : 2016/11/7 17 46
 */

public class ClockView extends View {
    private static final int H_POINTER_WIDTH = 15; //时针宽
    private static final int M_POINTER_WIDTH = 10; //分针宽
    private static final int S_POINTER_WIDTH = 5; //秒针宽
    private static final int SCALE_LINE_LENGTH = 50; //刻度线长
    private static final int SCALE_LINE_WIDTH = 6; //刻度线宽
    private static final int M_S_DEGREES_UNIT = 360 / 60; //分、秒针每个数字走的角度
    private static final int H_DEGREES_UNIT = 360 / 12; //时针每个数字走的角度
    private Paint mPaint;
    private int mW, mH, mCx, mCy, mR;
    private int mCount;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            postInvalidate();
        }
    };

    public ClockView(Context context) {
        this(context, null);
    }

    public ClockView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ClockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        mPaint = new Paint();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mW = getWidth();
        mH = getHeight();

        mR = Math.min(mW, mH) / 2 - 20;
        mCx = mW / 2;
        mCy = mH / 2;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        long beginTime = SystemClock.uptimeMillis();

        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mCx, mCy, mR, mPaint); //外圆
        mPaint.setColor(Color.BLACK);
        canvas.drawCircle(mCx, mCy, mR / 12, mPaint); //圆心

        /*
        绘制刻度
         */
        for (int i = 0; i < 60; i++) {
            canvas.save();
            canvas.rotate(M_S_DEGREES_UNIT * i, mCx, mCy);
            mPaint.setStrokeWidth(SCALE_LINE_WIDTH);
            canvas.drawLine(mCx, mCy - mR, mCx, mCy - mR + SCALE_LINE_LENGTH, mPaint);
            if (i % 5 == 0) {
                mPaint.setStrokeWidth(SCALE_LINE_WIDTH + 5);
                canvas.drawLine(mCx, mCy - mR, mCx, mCy - mR + SCALE_LINE_LENGTH + 10, mPaint);

                String num = i == 0 ? 12 + "" : i / 5 + "";
                float x = mCx - mPaint.measureText(num) / 2;
                float y = mCy - mR + SCALE_LINE_LENGTH + 30;
                mPaint.setTextSize(50);
                canvas.drawText(num, x, y + Math.abs(mPaint.ascent()), mPaint);
            }
            canvas.restore();

        }

        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
        int hour = calendar.get(Calendar.HOUR);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);

        /*
        分针在最下面 最先绘制
         */
        canvas.save();
        float mDegrees = minute * M_S_DEGREES_UNIT;
        canvas.rotate(mDegrees, mCx, mCy);
        mPaint.setColor(Color.GREEN);
        Path p = new Path();
        p.addRect(new RectF(mCx - M_POINTER_WIDTH, mCy - mR/4*3, mCx + M_POINTER_WIDTH, mCy + mR/5), Path.Direction.CW);
        p.quadTo(mCx, mCy - mR/4*3 - 40, mCx + M_POINTER_WIDTH, mCy - mR/4*3);
        canvas.drawPath(p, mPaint);
        canvas.restore();

        /*
        时针在中间
            分针60分走360度; 时针1小时(即60分)走30度
            mDegrees:hDegrees = 360:30 = 12:1
            hDegrees = mDegrees / 12; 时针相对于分钟数所要走的角度
         */
        canvas.save();
        canvas.rotate(mDegrees / 12 + hour * H_DEGREES_UNIT, mCx, mCy);
        mPaint.setColor(Color.RED);
        p = new Path();
        p.addRect(new RectF(mCx - H_POINTER_WIDTH, mCy - mR/3*2, mCx + H_POINTER_WIDTH, mCy + mR/5), Path.Direction.CW);
        p.quadTo(mCx, mCy - mR/3*2 - 30, mCx + H_POINTER_WIDTH, mCy - mR/3*2);
        canvas.drawPath(p, mPaint);
        canvas.restore();

        /*
        秒针在最上面
         */
        canvas.save();
        canvas.rotate(second * M_S_DEGREES_UNIT, mCx, mCy);
        mPaint.setColor(Color.BLACK);
        p = new Path();
        p.addRect(new RectF(mCx - S_POINTER_WIDTH, mCy - mR/5*4, mCx + S_POINTER_WIDTH, mCy + mR/5), Path.Direction.CW);
        p.quadTo(mCx, mCy - mR/5*4 - 30, mCx + S_POINTER_WIDTH, mCy - mR/5*4);
        canvas.drawPath(p, mPaint);
        canvas.restore();

        long spanTime = SystemClock.uptimeMillis() - beginTime;
//        System.out.println("间隔时间" + spanTime);
        if (mCount < 5) {//12的位置开始绘制时位置不对,刷新一次后就正常,这里让其快速刷新几次
            mHandler.sendEmptyMessage(0);
            mCount++;
        } else
            mHandler.sendEmptyMessageDelayed(0, 1000 - spanTime);
    }

}

那哥们的时针角度计算是:hour*unit+minute/2;   我这里按比例算的  :)

链接下他的博文:android 自定义view实现表盘效果   里面有较具体的说明


我的自定义View项目地址: https://github.com/aa86799/MyCustomView (欢迎start&fork)

本文地址:https://github.com/aa86799/MyCustomView/tree/master/clock

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值