android 自定义view实现表盘效果

来源:互联网 发布:python 爬虫 去标签 编辑:程序博客网 时间:2024/04/30 00:38

周末没事在家想干点啥,记得一年多前面试,那公司直接发个面试题让我做,其中就有让我做一个简单的表盘效果,不过当时没做出来,所以也没好意思去面试,今天就实现下,大概分如下几步

第一步:画一个简单的圆

第二步:绘制刻度

第三步:绘制时,分,表指针

第四步:绘制当前时间文字

第五步:实现时间动态显示


第一步画一个圆是很简单的,

package com.example.clockview;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.widget.ImageView;/** * Created by Adminis on 2016/11/6. */public class ClockView extends ImageView {    private static final String TAG = "ClockView";    private Paint mPaint;    private int widhth = 200;//控件的宽度    private int height = 200;//控件的高度    private int padding = 5;    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);        initPaint();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(widhth, height);    }    private void initPaint() {        mPaint = new Paint();        mPaint.setStrokeWidth(3);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.parseColor("#666666"));        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        drawCircle(canvas);    }    /**     * 绘制圆     * @param canvas     */    private void drawCircle(Canvas canvas) {         mPaint.setStyle(Paint.Style.STROKE);         canvas.drawCircle(widhth/2,height/2,widhth/2-padding,mPaint);    }}
效果图:


第二步:绘制刻度

/** * 绘制刻度 * @param canvas */private void drawScale(Canvas canvas) {    mPaint.setStyle(Paint.Style.FILL);    canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);}
效果图:


但是我们要画类似这种图:


一起要画12个这个线,那就相当于每二根线之间的角度就是360/12=30度

分析如图:


每换完一根线后画布就旋转30度

/** * 绘制刻度 * @param canvas */private void drawScale(Canvas canvas) {    mPaint.setStyle(Paint.Style.FILL);    for(int i=0;i<12;i++){        if(i%3==0){//  12  3  6  9对应的线长点            canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);        }else{            canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);        }        canvas.rotate(30, widhth / 2, widhth / 2);    }}
效果:


刻度就是利用了canvas的rotate()旋转方法绘制上去的,但是得以圆的中心点为旋转点

第三步绘制时 分 表 表针

这个绘制是根据当前的时间来指向的,

/** * 绘制时  分 表 指针 * @param canvas */private void drawPointer(Canvas canvas) {    mCalendar = Calendar.getInstance();    mHour = mCalendar.get(Calendar.HOUR);    mMinuate = mCalendar.get(Calendar.MINUTE);    mSecond = mCalendar.get(Calendar.SECOND);    //小时的旋转度    mDegrees = mHour*30+mMinuate/2;    mPaint.setColor(Color.BLACK);    canvas.save();    canvas.rotate(mDegrees, widhth / 2, widhth / 2);    canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);    canvas.restore();    //分钟    mPaint.setColor(Color.RED);    mDegrees = mMinuate*6+mSecond/10;    canvas.save();    canvas.rotate(mDegrees, widhth / 2, widhth / 2);    canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);    canvas.restore();    //绘制表针    mPaint.setColor(Color.BLUE);    mDegrees = mSecond*6;    canvas.save();    canvas.rotate(mDegrees, widhth / 2, widhth / 2);    canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);    canvas.restore();}
效果图:


在这解释下这个mDegrees这个值 比如现在是21:20 但是分为12小时制的话就是9:20  时就是9*30度=270度 但是这个时针指向9肯定是不对的,因为还有20分钟呢?时针线肯定是在9和10之间指向,这么这1小时之间是30度,就拿21:30这个时间来算,这个时候时针角度应该是30*9+30/2=285度  这就说明60分钟1小时 而1小时是30度,也就是说每2分钟时针角度应该动一下,根据这个原理分钟和表之间的计算也是类似的,

还有一点要特别的注意2就是绘制完时针它是根据当前的时针数比如9然后旋转30*9的 但是canvas使用了动画完以后一定要恢复到原来的,不然你绘制分钟就会出问题,我就遇到了这个问题,在这特别提醒下

特别改动下了:类全部代码复制下看下效果:

package com.example.clockview;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.widget.ImageView;import java.util.Calendar;/** * Created by Adminis on 2016/11/6. */public class ClockView extends ImageView {    private static final String TAG = "ClockView";    private Paint mPaint;    private int widhth = 200;//控件的宽度    private int height = 200;//控件的高度    private int padding = 5;    private Calendar mCalendar;    private int mHour;//小时    private int mMinuate;//分钟    private int mSecond;//秒    private float mDegrees ;//因为圆是360度 我们有12个刻度 所以就是360/12    private int mHourLineLen;//时指针 线    private int mMinuateLine;//分钟 线    private int mSecondLine ;//表钟线    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);        initPaint();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(widhth, height);        mHourLineLen = (int) (widhth/2*0.6);        mMinuateLine = (int) (widhth/2*0.7);        mSecondLine = (int) (widhth/2*0.8);    }    private void initPaint() {        mPaint = new Paint();        mPaint.setStrokeWidth(3);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.parseColor("#666666"));        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        drawCircle(canvas);        drawScale(canvas);        canvasCenterCircle(canvas);        drawPointer(canvas);    }    /**     * 在 圆中心绘制一个点     * @param canvas     */    private void canvasCenterCircle(Canvas canvas) {        mPaint.setStyle(Paint.Style.FILL);        canvas.drawCircle(widhth / 2, height / 2, 5, mPaint);    }    /**     * 绘制圆     * @param canvas     */    private void drawCircle(Canvas canvas) {         mPaint.setStyle(Paint.Style.STROKE);        canvas.drawCircle(widhth / 2, height / 2, widhth / 2 - padding, mPaint);    }    /**     * 绘制刻度     * @param canvas     */    private void drawScale(Canvas canvas) {        mPaint.setStyle(Paint.Style.FILL);        for(int i=0;i<12;i++){            if(i%3==0){//  12  3  6  9对应的线长点                canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);            }else{                canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);            }            canvas.rotate(30, widhth / 2, widhth / 2);        }    }    /**     * 绘制时  分 表 指针     * @param canvas     */    private void drawPointer(Canvas canvas) {        mCalendar = Calendar.getInstance();        mHour = mCalendar.get(Calendar.HOUR);        mMinuate = mCalendar.get(Calendar.MINUTE);        mSecond = mCalendar.get(Calendar.SECOND);        //小时的旋转度        mDegrees = mHour*30+mMinuate/2;        mPaint.setColor(Color.BLACK);        canvas.save();        canvas.rotate(mDegrees, widhth / 2, widhth / 2);        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);        canvas.restore();        //分钟        mPaint.setColor(Color.parseColor("#666666"));        mPaint.setStrokeWidth(5);        mDegrees = mMinuate*6+mSecond/10;        canvas.save();        canvas.rotate(mDegrees, widhth / 2, widhth / 2);        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mMinuateLine, mPaint);        canvas.restore();        //绘制表针        mPaint.setStrokeWidth(2);        mPaint.setColor(Color.parseColor("#666666"));        mDegrees = mSecond*6;        canvas.save();        canvas.rotate(mDegrees, widhth / 2, widhth / 2);        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mSecondLine, mPaint);        canvas.restore();    }}
效果:


第四步就是绘制当前时间文字显示在中心点下面:

/** * 绘制文字 * @param canvas */private void drawStr(Canvas canvas) {    mPaint.setTextSize(24);    StringBuffer sb =  new StringBuffer();    if(mHour<10){        sb.append("0").append(String.valueOf(mHour)).append(":");    }else{        sb.append(String.valueOf(mHour)).append(":");    }    if(mMinuate<10){        sb.append("0").append(String.valueOf(mMinuate)).append(":");    }else{        sb.append(String.valueOf(mMinuate)).append(":");    }    if(mSecond<10){        sb.append("0").append(String.valueOf(mSecond));    }else{        sb.append(String.valueOf(mSecond));    }    String str = sb.toString();    int strW = (int) mPaint.measureText(str);    canvas.drawText(str, widhth / 2 - strW / 2, widhth / 2 + 30,mPaint);}
效果:


最后一步就是利用Handler每秒去刷新界面就能做到动态的显示时间了:

最后完整的代码:

package com.example.clockview;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.widget.ImageView;import java.util.Calendar;/** * Created by Adminis on 2016/11/6. */public class ClockView extends ImageView {    private static final String TAG = "ClockView";    private Paint mPaint;    private int widhth = 200;//控件的宽度    private int height = 200;//控件的高度    private int padding = 5;    private Calendar mCalendar;    private int mHour;//小时    private int mMinuate;//分钟    private int mSecond;//秒    private float mDegrees ;//因为圆是360度 我们有12个刻度 所以就是360/12    private int mHourLineLen;//时指针 线    private int mMinuateLine;//分钟 线    private int mSecondLine ;//表钟线    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            invalidate();        }    };    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);        initPaint();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(widhth, height);        mHourLineLen = (int) (widhth/2*0.6);        mMinuateLine = (int) (widhth/2*0.7);        mSecondLine = (int) (widhth/2*0.8);    }    private void initPaint() {        mPaint = new Paint();        mPaint.setStrokeWidth(3);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.parseColor("#666666"));        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        drawCircle(canvas);        drawScale(canvas);        canvasCenterCircle(canvas);        drawPointer(canvas);        drawStr(canvas);        mHandler.sendEmptyMessage(1);    }    /**     * 绘制文字     * @param canvas     */    private void drawStr(Canvas canvas) {        mPaint.setTextSize(24);        StringBuffer sb =  new StringBuffer();        if(mHour<10){            sb.append("0").append(String.valueOf(mHour)).append(":");        }else{            sb.append(String.valueOf(mHour)).append(":");        }        if(mMinuate<10){            sb.append("0").append(String.valueOf(mMinuate)).append(":");        }else{            sb.append(String.valueOf(mMinuate)).append(":");        }        if(mSecond<10){            sb.append("0").append(String.valueOf(mSecond));        }else{            sb.append(String.valueOf(mSecond));        }        String str = sb.toString();        int strW = (int) mPaint.measureText(str);        canvas.drawText(str, widhth / 2 - strW / 2, widhth / 2 + 30,mPaint);    }    /**     * 在 圆中心绘制一个点     * @param canvas     */    private void canvasCenterCircle(Canvas canvas) {        mPaint.setStyle(Paint.Style.FILL);        canvas.drawCircle(widhth / 2, height / 2, 5, mPaint);    }    /**     * 绘制圆     * @param canvas     */    private void drawCircle(Canvas canvas) {         mPaint.setStyle(Paint.Style.STROKE);        canvas.drawCircle(widhth / 2, height / 2, widhth / 2 - padding, mPaint);    }    /**     * 绘制刻度     * @param canvas     */    private void drawScale(Canvas canvas) {        mPaint.setStyle(Paint.Style.FILL);        for(int i=0;i<12;i++) {            if (i % 3 == 0) {//  12  3  6  9对应的线长点                canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);            }else{                canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);            }            canvas.rotate(30, widhth / 2, widhth / 2);        }    }    /**     * 绘制时  分 表 指针     * @param canvas     */    private void drawPointer(Canvas canvas) {        mCalendar = Calendar.getInstance();        mHour = mCalendar.get(Calendar.HOUR);        mMinuate = mCalendar.get(Calendar.MINUTE);        mSecond = mCalendar.get(Calendar.SECOND);        //小时的旋转度        mDegrees = mHour*30+mMinuate/2;        mPaint.setColor(Color.BLACK);        canvas.save();        canvas.rotate(mDegrees, widhth / 2, widhth / 2);        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);        canvas.restore();        //分钟        mPaint.setColor(Color.parseColor("#666666"));        mPaint.setStrokeWidth(5);        mDegrees = mMinuate*6+mSecond/10;        canvas.save();        canvas.rotate(mDegrees, widhth / 2, widhth / 2);        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mMinuateLine, mPaint);        canvas.restore();        //绘制表针        mPaint.setStrokeWidth(2);        mPaint.setColor(Color.parseColor("#666666"));        mDegrees = mSecond*6;        canvas.save();        canvas.rotate(mDegrees, widhth / 2, widhth / 2);        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mSecondLine, mPaint);        canvas.restore();    }}
动态效果:


终于写完了!


刚群里有人反映说3时和15秒时时针和表针线不重合的问题,于是我自己写死了一个数据 测试了下发现真的是有这个bug 导致这个bug造成的原因是

/** * 绘制刻度 * @param canvas */private void drawScale(Canvas canvas) {    mPaint.setStyle(Paint.Style.FILL);    for(int i=0;i<12;i++) {        if (i % 3 == 0) {//  12  3  6  9对应的线长点            canvas.drawLine(widhth / 2-padding , padding, widhth / 2 -padding, padding + 4 + 15, mPaint);        }else{            canvas.drawLine(widhth / 2-padding , padding, widhth / 2-padding, padding + 4 + 8, mPaint);        }        canvas.rotate(30, widhth / 2, widhth / 2);    }}
是这个x轴开始和结束点的坐标不对,应该把-padding去掉就正常了

改成后:

/** * 绘制刻度 * @param canvas */private void drawScale(Canvas canvas) {    mPaint.setStyle(Paint.Style.FILL);    for(int i=0;i<12;i++) {        if (i % 3 == 0) {//  12  3  6  9对应的线长点            canvas.drawLine(widhth / 2-padding , padding, widhth / 2 -padding, padding + 4 + 15, mPaint);        }else{            canvas.drawLine(widhth / 2-padding , padding, widhth / 2-padding, padding + 4 + 8, mPaint);        }        canvas.rotate(30, widhth / 2, widhth / 2);    }}
我现在把时针写死在3点 秒数定在15秒后效果:


谢谢群里提出问题的同学






3 0