android 自定义 view 实现表盘效果

第一步:画一个简单的圆

第二步:绘制刻度

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

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

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


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

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秒后效果:

自定义View之仿支付宝芝麻信用分仪表盘效果,喜欢的话,请给个star,谢谢.使用添加项目依赖Add it in your root build.gradle at the end of repositories: allprojects {         repositories { ... maven { url "https://jitpack.io" }         }     } Add the dependency     dependencies {             compile 'com.github.HotBitmapGG:CreditSesameRingView:V1.0.2' }新版芝麻信用分使用     // The gradient color can define your own private final int[] mColors = new int[]{             0xFFFF80AB,             0xFFFF4081,             0xFFFF5177,             0xFFFF7997              }; // Set up the need to score mLayout = (RelativeLayout) view.findViewById(R.id.layout);       mButton = (Button) view.findViewById(R.id.btn);       newCreditSesameView = (NewCreditSesameViewview.findViewById(R.id.sesame_view);       mLayout.setBackgroundColor(mColors[0]);       mButton.setOnClickListener(new View.OnClickListener()       { @Override public void onClick(View view)            {                newCreditSesameView.setSesameValues(639);                startColorChangeAnim();            }       }); // The background color gradient animation Simply illustrates the effect Can customize according to your need public void startColorChangeAnim()      { ObjectAnimator animator = ObjectAnimator.ofInt(mLayout, "backgroundColor", mColors);          animator.setDuration(3000);          animator.setEvaluator(new ArgbEvaluator());          animator.start();      }旧版芝麻信用分使用      // Set up the need to score oldCreditSesameView = (OldCreditSesameViewview.findViewById(R.id.sesame_view);       mButton = (Button) view.findViewById(R.id.btn);       mButton.setOnClickListener(new View.OnClickListener()       { @Override public void onClick(View view)           {               oldCreditSesameView.setSesameValues(639);           }       });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值