华为时钟(高仿)

手痒,撸个华为时钟玩玩,顺便强化下自定义view的技能,先把效果图和源码地址贴出来,有空再讲解实现思路。

华为时钟常规模式:

华为时钟深色模式:

高仿华为时钟效果图:

代码实现逻辑如下:

public class HwClock extends View {
    // 默认高度
    public final int defaultHeight = SizeUtils.dp2px(getContext(), 300);
    //  默认宽度
    public final int defaultWidth = SizeUtils.dp2px(getContext(), 300);
    // 时钟距离时钟View的边距
    public final int outMargin = SizeUtils.dp2px(getContext(), 20);
    // 时钟数字距离时钟刻度的边距
    public final int inMargin = SizeUtils.dp2px(getContext(), 15);
    // 时针刻度的长度
    public final int hourScaleLength = SizeUtils.dp2px(getContext(), 12);
    // 秒针刻度的长度
    public final int secondScaleLength = SizeUtils.dp2px(getContext(), 6);
    // 内指针距离指针的边距
    public final int inHandMargin = SizeUtils.dp2px(getContext(), 1);
    // 时钟数字的大小
    public final int timeTextSize = 40;

    // 是否是深色模式
    public boolean isDarkMode;
    // View半径
    public int viewRadius;
    // 时钟半径
    public int clockRadius;
    // 刻度画笔
    private Paint scalePaint;
    // 时钟背景画笔
    private Paint clockBgPaint;
    // 画时钟数字画笔
    private Paint timeTextPaint;
    // 时针画笔
    private Paint hourHandPaint;
    // 时针画笔2
    private Paint hourHandPaint2;
    // 分针画笔
    private Paint minuteHandPaint;
    // 分针画笔2
    private Paint minuteHandPaint2;
    // 秒针画笔
    private Paint secondHandPaint;
    // 秒针画笔2
    private Paint secondHandPaint2;

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

    public HwClock(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HwClock(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaints();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 用下而上控制绘制层级
        drawBackground(canvas);
        drawHourHand(canvas);
        drawMinuteHand(canvas);
        drawSecondHand(canvas);

        // 延迟一秒绘制一次,以此往复
        postInvalidateDelayed(1000);
    }

    /**
     * 设置时钟深色模式
     */
    public void setDarkMode(boolean isDark) {
        this.isDarkMode = isDark;
        initPaints();
        invalidate();
    }

    /**
     * 初始化画笔
     */
    private void initPaints() {
        scalePaint = new Paint();
        // 设置笔帽为椭圆
        scalePaint.setStrokeCap(Paint.Cap.ROUND);
        // 抗锯齿
        scalePaint.setAntiAlias(true);
        scalePaint.setStrokeWidth(3);

        clockBgPaint = new Paint();
        clockBgPaint.setAntiAlias(true);
        clockBgPaint.setColor(Color.BLACK);

        timeTextPaint = new Paint();
        timeTextPaint.setAntiAlias(true);
        timeTextPaint.setStrokeCap(Paint.Cap.ROUND);
        timeTextPaint.setTextSize(timeTextSize);

        hourHandPaint = new Paint();
        hourHandPaint.setAntiAlias(true);
        hourHandPaint.setStrokeCap(Paint.Cap.ROUND);
        hourHandPaint.setStrokeWidth(25);
        hourHandPaint2 = new Paint();
        hourHandPaint2.setAntiAlias(true);
        hourHandPaint2.setStrokeCap(Paint.Cap.ROUND);
        hourHandPaint2.setStrokeWidth(10);

        minuteHandPaint = new Paint();
        minuteHandPaint.setAntiAlias(true);
        minuteHandPaint.setStrokeCap(Paint.Cap.ROUND);
        minuteHandPaint.setStrokeWidth(20);
        minuteHandPaint2 = new Paint();
        minuteHandPaint2.setAntiAlias(true);
        minuteHandPaint2.setStrokeCap(Paint.Cap.ROUND);
        minuteHandPaint2.setStrokeWidth(8);

        secondHandPaint = new Paint();
        secondHandPaint.setAntiAlias(true);
        secondHandPaint.setStrokeCap(Paint.Cap.ROUND);
        secondHandPaint.setColor(Color.RED);
        secondHandPaint.setStrokeWidth(5);
        secondHandPaint2 = new Paint();
        secondHandPaint2.setAntiAlias(true);

        if (isDarkMode) {
            timeTextPaint.setColor(Color.WHITE);
            hourHandPaint.setColor(Color.WHITE);
            hourHandPaint2.setColor(Color.BLACK);
            minuteHandPaint.setColor(Color.WHITE);
            minuteHandPaint2.setColor(Color.BLACK);
        } else {
            timeTextPaint.setColor(Color.BLACK);
            hourHandPaint.setColor(Color.BLACK);
            hourHandPaint2.setColor(Color.WHITE);
            minuteHandPaint.setColor(Color.BLACK);
            minuteHandPaint2.setColor(Color.WHITE);
        }
    }

    /**
     * 画背景
     */
    private void drawBackground(Canvas canvas) {
        if (isDarkMode) {
            canvas.drawCircle(viewRadius, viewRadius, clockRadius + SizeUtils.dp2px(getContext(), 5), clockBgPaint);
        }
        drawClockScale(canvas);
        drawTimeText(canvas);
    }

    /**
     * 画时钟刻度
     */
    private void drawClockScale(Canvas canvas) {
        canvas.save();
        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) {
                if (isDarkMode) {
                    scalePaint.setColor(Color.WHITE);
                } else {
                    scalePaint.setColor(Color.BLACK);
                }
                // 时针刻度绘制
                canvas.drawLine(viewRadius, outMargin, viewRadius, outMargin + hourScaleLength, scalePaint);
            } else {
                scalePaint.setColor(Color.GRAY);
                // 秒针刻度绘制
                canvas.drawLine(viewRadius, outMargin, viewRadius, outMargin + secondScaleLength, scalePaint);
            }
            canvas.rotate(6, viewRadius, viewRadius);
        }
        canvas.restore();
    }

    /**
     * 画时钟数字
     */
    private void drawTimeText(Canvas canvas) {
        String timeText;
        for (int i = 0; i < 12; i++) {
            if (i == 0) {
                timeText = "12";
            } else {
                timeText = i + "";
            }
            // 三角函数边长C
            int lengthC = viewRadius - (outMargin + hourScaleLength + inMargin);
            double[] results = calculateTimeTextPosition(lengthC, i * 30, timeText);
            canvas.drawText(timeText, Float.parseFloat(results[0] + ""), Float.parseFloat(results[1] + ""), timeTextPaint);
        }
    }

    /**
     * 计算时钟数字坐标
     *
     * @param lengthC  三角函数边长C
     * @param degrees  夹角
     * @param timeText 时间数字
     * @return int[0] = x坐标,int[2] = y坐标
     */
    private double[] calculateTimeTextPosition(int lengthC, int degrees, String timeText) {
        // 获取文字宽高
        Rect timeTextRect = new Rect();
        timeTextPaint.getTextBounds(timeText, 0, timeText.length(), timeTextRect);
        int timeTextWidth = timeTextRect.width();
        int timeTextHeight = timeTextRect.height();
        // 利用三角函数计算文字坐标
        double[] results = new double[2];
        // x轴坐标 = sin*lengthC+view半径-文字一半宽度
        results[0] = Math.sin(Math.PI / 180 * degrees) * lengthC + viewRadius - timeTextWidth / 2d;
        // y轴坐标 = view半径-cos*lengthC+文字一半高度
        results[1] = viewRadius - Math.cos(Math.PI / 180 * degrees) * lengthC + timeTextHeight / 2d;
        return results;
    }


    /**
     * 画时针
     * 时针长度设置为时钟半径的2/3
     * 内时针长度设置为时针长度的1/4
     */
    private void drawHourHand(Canvas canvas) {
        canvas.save();
        Calendar calendar = Calendar.getInstance();
        int hours = calendar.get(Calendar.HOUR);
        int minutes = calendar.get(Calendar.MINUTE);

        // 这里计算时钟总和,在不同的区间内时针显示的阴影方向不同
        float totalHours = minutes == 0 ? hours : hours + minutes / 60f;
        if (!isDarkMode) {
            if (totalHours % 6 == 0) {
                hourHandPaint.setShadowLayer(8, 0, 0, Color.GRAY);
            } else if (totalHours < 6) {
                hourHandPaint.setShadowLayer(8, 5, -5, Color.GRAY);
            } else {
                hourHandPaint.setShadowLayer(8, -5, -5, Color.GRAY);
            }
        }

        int hourHandLength = clockRadius * 2 / 3;
        int hourHandLength2 = hourHandLength / 4;
        canvas.rotate((hours + minutes / 60f) * 30, viewRadius, viewRadius);
        canvas.drawLine(viewRadius, viewRadius - hourHandLength, viewRadius, viewRadius, hourHandPaint);
        canvas.drawLine(viewRadius, viewRadius - hourHandLength + inHandMargin, viewRadius, viewRadius - hourHandLength + inHandMargin + hourHandLength2, hourHandPaint2);
        canvas.restore();
    }

    /**
     * 画分针
     * 分针长度设置为时钟半径的4/5
     * 内分针长度设置为分针长度的1/5
     */
    private void drawMinuteHand(Canvas canvas) {
        canvas.save();
        Calendar calendar = Calendar.getInstance();
        int minutes = calendar.get(Calendar.MINUTE);
        int seconds = calendar.get(Calendar.SECOND);

        // 这里计算分钟总和,在不同的区间内分针显示的阴影方向不同
        float totalMinutes = seconds == 0 ? minutes : minutes + seconds / 60f;
        if (!isDarkMode) {
            if (totalMinutes % 30 == 0) {
                minuteHandPaint.setShadowLayer(8, 0, 0, Color.GRAY);
            } else if (totalMinutes < 30) {
                minuteHandPaint.setShadowLayer(8, 5, -5, Color.GRAY);
            } else {
                minuteHandPaint.setShadowLayer(8, -5, -5, Color.GRAY);
            }
        }

        int minuteHandLength = clockRadius * 4 / 5;
        int minuteHandLength2 = minuteHandLength / 4;
        canvas.rotate((minutes + seconds / 60f) * 6, viewRadius, viewRadius);
        canvas.drawLine(viewRadius, viewRadius - minuteHandLength, viewRadius, viewRadius, minuteHandPaint);
        canvas.drawLine(viewRadius, viewRadius - minuteHandLength + inHandMargin, viewRadius, viewRadius - minuteHandLength + inHandMargin + minuteHandLength2, minuteHandPaint2);
        canvas.restore();
    }

    /**
     * 画秒针
     */
    private void drawSecondHand(Canvas canvas) {
        canvas.save();
        Calendar calendar = Calendar.getInstance();
        int seconds = calendar.get(Calendar.SECOND);

        // 在不同的区间内秒针显示的阴影方向不同
        if (!isDarkMode) {
            if (seconds % 30 == 0) {
                secondHandPaint.setShadowLayer(8, 0, 0, Color.GRAY);
            } else if (seconds < 30) {
                secondHandPaint.setShadowLayer(8, 5, -5, Color.GRAY);
            } else {
                secondHandPaint.setShadowLayer(8, -5, -5, Color.GRAY);
            }
        }

        canvas.rotate(seconds * 6, viewRadius, viewRadius);
        canvas.drawLine(viewRadius, outMargin, viewRadius, viewRadius + SizeUtils.dp2px(getContext(), 10), secondHandPaint);
        secondHandPaint2.setColor(Color.RED);
        canvas.drawCircle(viewRadius, viewRadius, SizeUtils.dp2px(getContext(), 5), secondHandPaint2);
        secondHandPaint2.setColor(Color.WHITE);
        canvas.drawCircle(viewRadius, viewRadius, SizeUtils.dp2px(getContext(), 2), secondHandPaint2);
        canvas.restore();
    }


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

    /**
     * 重新测量
     */
    private void reMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 测量宽度
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        // 测量高度
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            // 最大或者未指定大小,这种就设置默认大小
            setMeasuredDimension(defaultWidth, defaultHeight);
        } else if (widthMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.EXACTLY) {
            // 确定大小,取宽高中的最小值
            int min = Math.min(measureWidth, measureHeight);
            setMeasuredDimension(min, min);
        } else {
            setMeasuredDimension(defaultWidth, defaultHeight);
        }

        viewRadius = getMeasuredWidth() / 2;
        // 时钟半径 = View宽高/2 - 外边距
        clockRadius = getMeasuredWidth() / 2 - outMargin;
    }

}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值