自定义View之钟表盘实现

效果图预览
gif图片录制的不是很清晰
这里写图片描述

1. 分析和技术实现

1. 绘制一个外圆和一个红色的内圆,还需要绘制中间的一个小圆点
2. 绘制刻度,这里我才用了旋转画布的操作来简化计算
3. 主要就是canvas的操作,canvas.save和canvas.restore成对出现,保证当前画布操作不会影响其他的
4. 因为秒钟要每秒更新一次,需要调用postInvalidateDelayed(1000)一秒钟发一次延迟消息 刷新控件

2. 初始化一些东西

//外面圆的半径
private int mOutCircleRadius;
//钟表的半径
private int mClockRadius;
//中间小圆点半径
private int mCenterRadius;
//圆心坐标
private int mCenterX;
private int mCenterY;
//时钟刻度线的高度
private int mScaleLineHeight;
//画笔对象
private Paint mClockPaint;
private Paint mCirclePaint;
private Paint mTimePaint;
//时间操作辅助类
private Calendar mCalendar;
//刻度线数量 钟表要分多少等分 一般是12根线
private static final int SCALE_NUM = 12;
//外圆和钟表的边的宽度
private int mOutCircleStrokeWidth;
private int mClockStrokeWidth;
//初始化各种颜色值
private int mClockColor;
private int mCircleColor;
private int mHourColor;
private int mMinuteColor;
private int mSecondColor;
private int mScaleColor;
private int mMidCircleColor;

3. 自定义属性值


<?xml version="1.0" encoding="utf-8"?>
<resources>

   <declare-styleable name="ClockView">
       <attr name="hour_line_color" format="color"/>
       <attr name="minute_line_color" format="color"/>
       <attr name="second_line_color" format="color"/>
       <attr name="scale_line_color" format="color"/>
       <attr name="clock_color" format="color"/>
       <attr name="out_circle_color" format="color"/>
       <attr name="mid_circle_color" format="color"/>
   </declare-styleable>
</resources>

代码中取值

private void initAttrs(AttributeSet attrs) {

       TypedArray typedArray = getContext().obtainStyledAttributes(attrs,R.styleable.ClockView);
       mClockColor = typedArray.getColor(R.styleable.ClockView_clock_color, Color.RED);
       mCircleColor = typedArray.getColor(R.styleable.ClockView_out_circle_color, Color.GRAY);
       mHourColor = typedArray.getColor(R.styleable.ClockView_hour_line_color, Color.RED);
       mMinuteColor = typedArray.getColor(R.styleable.ClockView_minute_line_color, Color.BLUE);
       mSecondColor = typedArray.getColor(R.styleable.ClockView_second_line_color, Color.GREEN);
       mScaleColor = typedArray.getColor(R.styleable.ClockView_scale_line_color, Color.RED);
       mMidCircleColor = typedArray.getColor(R.styleable.ClockView_mid_circle_color, Color.RED);
       //记得回收 不然有可能造成内存泄漏
       typedArray.recycle();
}

4.onMeasure和onSizeChanged处理

onMeasure主要是处理wrap_content的情况,如果宽高为
wrap_content的情况下我给默认指定了大小

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    //当宽度为wrap_content时我们给一个默认值
    int width = dp2px(140);
    int height = dp2px(140);
    width = widthMode == MeasureSpec.AT_MOST ? width : widthSize;
    height = heightMode == MeasureSpec.AT_MOST ? height : heightSize;
    setMeasuredDimension(width,height);
}

//根据控件宽高计算外圆半径和钟表的半径
 @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    mCenterX = w / 2;
    mCenterY = h / 2;
    //外圆半径 = 控件宽度的一半 - 外圆边的宽度
    mOutCircleRadius = w / 2 - mOutCircleStrokeWidth;
    //钟表半径 = 控件宽度的一半 - 外圆边的宽度 - 钟表边的宽度
    mClockRadius = w / 2 - mOutCircleStrokeWidth - mClockStrokeWidth;
    mCenterRadius = dp2px(3);

    mScaleLineHeight = dp2px(6);
}

5. 绘制外圆,钟表圆和中心点

//绘制圆
 private void drawClockCircle(Canvas canvas) {

     canvas.save();
     //画钟表的圆
     mClockPaint.setColor(mClockColor);
     mClockPaint.setStrokeWidth(mClockStrokeWidth);
     canvas.drawCircle(0,0, mClockRadius, mClockPaint);

     //画钟表的外圆
     mCirclePaint.setStyle(Paint.Style.STROKE);
     mCirclePaint.setColor(mCircleColor);
     canvas.drawCircle(0,0, mOutCircleRadius, mCirclePaint);

     //画钟表的中心圆点
     mCirclePaint.setStyle(Paint.Style.FILL);
     mCirclePaint.setColor(mMidCircleColor);
     canvas.drawCircle(0,0, mCenterRadius,mCirclePaint);
     canvas.restore();
 }

6. 绘制钟表的刻度

钟表的每一根刻度线理论上都需要计算它的角度,然后计算它的x,y坐标值,但是计算量很大,我投机取巧,通过旋转画布的方式来简化绘制过程,比如我要绘制2点钟的刻度线 我顺时针旋转2 * 30 = 60就可以了, 从12点位置开始绘制

 //根据刻度线的数量来获取旋转画布的角度
float angle = 360 / SCALE_NUM;
for (int i = 0; i < SCALE_NUM; i++) {
    canvas.save();
    canvas.rotate(i * angle);
    //从12点位置开始绘制
    canvas.drawLine(0, - mClockRadius + lineSpacing, 0, - mClockRadius +
            mScaleLineHeight + lineSpacing,mClockPaint);
    canvas.restore();
}

//绘制钟表的刻度
private void drawClockScale(Canvas canvas) {
    mClockPaint.setStrokeWidth(dp2px(3));
    mClockPaint.setColor(mScaleColor);
    //刻度线与钟表的间距
    int lineSpacing = dp2px(6);
    //通过旋转画布的方式来简化绘制过程,不然得计算每一根刻度线的角度和x,y坐标值
    //比如我要绘制2点钟的刻度线 我顺时针旋转2 * 30 = 60就可以了
    //根据刻度线的数量来获取旋转画布的角度
    float angle = 360 / SCALE_NUM;
    for (int i = 0; i < SCALE_NUM; i++) {
        canvas.save();
        canvas.rotate(i * angle);
        //从12点位置开始绘制
        canvas.drawLine(0, - mClockRadius + lineSpacing, 0, - mClockRadius +
                mScaleLineHeight + lineSpacing,mClockPaint);
        canvas.restore();
    }
}

7. 绘制小时,分钟和秒钟的线

由Calendar.getInstance()获取Calendar对象
int hour = mCalendar.get(Calendar.HOUR);
int minute = mCalendar.get(Calendar.MINUTE);
int second = mCalendar.get(Calendar.SECOND);
分别获取当前的小时,分钟和秒的数值,然后根据当前的时分秒选中相应的角度,画线就这么简单
//一秒钟发一次延迟消息 刷新控件

postInvalidateDelayed(1000);

//绘制小时,分钟,秒的线
private void drawTimeScaleLine(Canvas canvas) {

    mCalendar = Calendar.getInstance();
    //绘制小时的刻度线
    canvas.save();
    mTimePaint.setStrokeWidth(dp2px(3));
    mTimePaint.setColor(mHourColor);
    int hour = mCalendar.get(Calendar.HOUR);
    //360 / 12小时 = 30度
    canvas.rotate(hour * 30f);
    //基于12点位置画线 所以x坐标就是圆心的x坐标 坐标系向下为正方向 所以y为负值
    canvas.drawLine(0,-dp2px(6),0, - mClockRadius * 0.50f,mTimePaint);
    canvas.restore();

    //绘制分钟的刻度线
    canvas.save();
    mTimePaint.setStrokeWidth(dp2px(2));
    mTimePaint.setColor(mMinuteColor);
    int minute = mCalendar.get(Calendar.MINUTE);
    //360 / 60分钟 = 6度
    canvas.rotate(minute * 6f);
    canvas.drawLine(0,-dp2px(6),0,- mClockRadius * 0.62f,mTimePaint);
    canvas.restore();

    //绘制秒钟的刻度线
    canvas.save();
    mTimePaint.setStrokeWidth(dp2px(1.5f));
    mTimePaint.setColor(mSecondColor);
    int second = mCalendar.get(Calendar.SECOND);
    Log.e("ClockView second",second+"");
    // 360 / 60秒 = 6度
    canvas.rotate(second * 6f);
    canvas.drawLine(0,-dp2px(6),0,- mClockRadius * 0.74f,mTimePaint);
    canvas.restore();

    //一秒钟发一次延迟消息 刷新控件
    postInvalidateDelayed(1000);
}

8.源代码下载

最后统一提供下载地址

9.联系方式

QQ:1509815887

猜你喜欢

转载自blog.csdn.net/rjgcszlc/article/details/80993684