唔得闲都要搞事之实现LED数字时钟

前言:实现完前篇文章中的荧光时钟后,突然想起大学期间做数字电路实验经常见到的LED数字显示屏,数字电路的课设也用 LED 数字结合简单的电路做了一个简单的老虎机游戏,甚是怀念。好吧!开始实现一个 简单的LED 数字时钟!

LED 数字时钟效果图

唔得闲都要搞事之实现LED数字时钟_第1张图片
image

1. 如何实现LED数字

LED 数字无非就是由七根线组成,只要根据数字0-9线条的组成特性,画出对应的线条即可得到相应数字。

基本思路:画完一个数字后,将画布移动一定的偏移量,继续画下一个数字,依次类推。由于画数字的动作其实是重复的,我们可以封装一个工具类NumPaintUtil来画数字。

每个LED数字自身的坐标系示意图参考如下,每根线的坐标请根据代码意会


唔得闲都要搞事之实现LED数字时钟_第2张图片
image
public class NumPaintUtil {


    private Canvas canvas;// view 的画布
    private float lineWidth;//线的长度
    private int lineColor;

    private float padding=10;//竖直间距,默认为10
    /**
     *
     * @param canvas 画布
     * @param lineWidth 线长
     * @param lineColor 线的颜色
     */
    public NumPaintUtil(Canvas canvas,float lineWidth,int lineColor){
        this.canvas=canvas;
        this.lineWidth=lineWidth;
        this.lineColor=lineColor;
    }

    /**
     *
     * @param lineWidth 线长
     * @param lineColor 线的颜色
     */
    public NumPaintUtil(float lineWidth,int lineColor){
        this.lineWidth=lineWidth;
        this.lineColor=lineColor;
    }

    public Canvas getCanvas() {
        return canvas;
    }

    /**
     *
     * 调用drawNumber之前需设置画布对象
     * @param canvas
     */
    public void setCanvas(Canvas canvas) {
        this.canvas = canvas;
    }

    /**
     *
     * 根据0-9的特性绘制线条从而实现对应的数字
* 使用前,需初始化设置canvas,并且请将 canvas 移动到合适的位置 * @param num 数字 */ public void drawNumber(int num){ if(canvas==null){ try{ throw new CanvasNullPointException("canvas is null,please init canvas"); }catch (CanvasNullPointException e){ e.printStackTrace(); } return; } switch (num) { case 0: /** * 去掉中间线即可 * —— * | | * | | * —— */ drawTopLeftLine(); drawTopLine(); drawBottomLine(); drawTopRightLine(); drawBottomLeftLine(); drawBottomRightLine(); break; case 1: /** * 只画右上和右下的线 * * | * | * */ drawTopRightLine(); drawBottomRightLine(); break; case 2: /** * 去掉左上和右下 * * —— * | * —— * | * —— * */ drawCenterLine(); drawTopLine(); drawBottomLine(); drawTopRightLine(); drawBottomLeftLine(); break; case 3: /** * 去掉左上和左下 * * —— * | * —— * | * —— * */ drawCenterLine(); drawTopLine(); drawBottomLine(); drawTopRightLine(); drawBottomRightLine(); break; case 4: /** * 去掉顶部、底部和左下 * * * | | * —— * | * */ drawCenterLine(); drawTopLeftLine(); drawTopRightLine(); drawBottomRightLine(); break; case 5: /** * 去掉右上和左下 * * —— * | * —— * | * —— * */ drawCenterLine(); drawTopLeftLine(); drawTopLine(); drawBottomLine(); drawBottomRightLine(); break; case 6: /** * 去掉右上 * * —— * | * —— * | | * —— * */ drawCenterLine(); drawTopLine(); drawBottomLine(); drawTopLeftLine(); drawBottomLeftLine(); drawBottomRightLine(); break; case 7: /** * —— * | * * | */ drawTopLine(); drawTopRightLine(); drawBottomRightLine(); break; case 8: /** * 全保留 * __ * | | * —— * | | * —— */ drawCenterLine(); drawTopLeftLine(); drawTopLine(); drawBottomLine(); drawTopRightLine(); drawBottomLeftLine(); drawBottomRightLine(); break; case 9: /** * 去掉左下 * __ * | | * —— * | * —— */ drawCenterLine(); drawTopLeftLine(); drawTopLine(); drawBottomLine(); drawTopRightLine(); drawBottomRightLine(); break; } } public class CanvasNullPointException extends NullPointerException{ public CanvasNullPointException(String msg){ super(msg); } } /** * 画中间线 * */ private void drawCenterLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(-lineWidth / 2, 0, lineWidth / 2, 0, numPaint); } /** * 画top 线 * */ private void drawTopLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(-lineWidth / 2, -lineWidth - padding, lineWidth / 2, -lineWidth - padding, numPaint); } /** * 画底部的线 * */ private void drawBottomLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(-lineWidth / 2, lineWidth + padding, lineWidth / 2, lineWidth + padding, numPaint); } /** * 画左上的线 * */ private void drawTopLeftLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(-lineWidth / 2, -padding/2, -lineWidth / 2, -padding/2 - lineWidth, numPaint); } /** * 画右上的线 * */ private void drawTopRightLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(lineWidth / 2, -padding/2, lineWidth / 2, -padding/2 - lineWidth, numPaint); } /** * 画左下 * */ private void drawBottomLeftLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(-lineWidth / 2, padding/2, -lineWidth / 2, padding/2 + lineWidth, numPaint); } /** * 画右下 * */ private void drawBottomRightLine() { Paint numPaint = getPaint(lineColor); canvas.drawLine(lineWidth / 2, padding/2, lineWidth / 2, padding/2 + lineWidth, numPaint); } /** * 获得对应颜色的画笔 * * @param color * @return */ public Paint getPaint(int color) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(color); paint.setStrokeWidth(5f); paint.setDither(true); return paint; } }

2.使用 NumPaintUtil 来逐步绘制LED时钟

2.1 第一步,自定义NumClock继承 View,实现以下构造方法

 public NumClock(Context context) {
        super(context);
    }

    public NumClock(Context context, AttributeSet attrs) {
        super(context, attrs);
        obtainStyledAttrs(attrs);
    }
    

2.2 第二步,添加必要属性

    //线的颜色
    private int lineColor;
    //中间两小点的颜色
    private int pointColor;
    //是否展示秒数
    private boolean showSeconds;
    //控件真实长宽
    private int mRealWidth, mRealHeight;
    private float centerX, centerY;
    private float lineWidth;//线的长度
    private float padding;//数字间的间距
    private float centerPadding;//时与分的间距
    

2.3 自定义 attr.xml 文件,增加以下属性,并获取样式


    
        
        
        
        
        
         
    
 /**
     * 获取样式,假如出现异常时取默认值
     *
     * @param attrs
     */
    private void obtainStyledAttrs(AttributeSet attrs) {
        TypedArray array;

        try {
            array = getContext().obtainStyledAttributes(attrs, R.styleable.NumClock);
            lineColor = array.getColor(R.styleable.NumClock_lineColor, Color.parseColor("#ffaacc"));
            showSeconds = array.getBoolean(R.styleable.NumClock_showSecond, false);
            pointColor = array.getColor(R.styleable.NumClock_pointColor, Color.parseColor("#ffaacc"));
        } catch (Exception e) {
            lineColor = Color.parseColor("#ffaacc");
            pointColor = Color.parseColor("#ffaacc");
            showSeconds = false;
        } 
    }

2.4 测量 view 的真实长宽,设置 view 的比例


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获得该view真实的宽度和高度
        mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
        mRealHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
        //取最小值
        int miniValue = Math.min(mRealHeight, mRealWidth);

        //设置长宽比为3:1
        int height = miniValue;
        int width = miniValue * 3;
        //更改 view 的长宽
        setMeasuredDimension(width, height);


    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //TODO 设置各个参数
        centerX = w / 2;
        centerY = h / 2;
        mRealWidth = w;
        mRealHeight = h;
        //设置每一根线的长度为 view 宽度的十分一
        lineWidth = w / 10;
        //数字间的间距
        padding = (mRealWidth - 4 * lineWidth) / 8f;
        //时与分之间的间距
        centerPadding = 2 * padding;
        //由于 onDraw 调用比较频繁,故不在 onDraw 中实例化
        numPaintUtil = new NumPaintUtil(lineWidth, lineColor);
    }

2.5 开始绘制

PS:以下代码中调用了多次 canvas的 translate 平移方法,请先了解translate方法的机制及用途(这是自定义view 的基础知识),本文不作阐述。



    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //需在此设置numPaintUtil的 canvas 属性
        numPaintUtil.setCanvas(canvas);
        //获取当前的时分秒
        Calendar calendar = Calendar.getInstance();
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        //锁定画布
        canvas.save();
        //为了减少坐标计算量,将坐标原点从(0,0)移动到 view 的中心点
        canvas.translate(centerX, centerY);
        //绘制时
        drawHour(canvas, hour);
        //是否闪烁点
        if (isShow) {
            drawPointBtHourNMinuter(canvas);
        }
        //更改标志位
        isShow = !isShow;
        //绘制分
        drawMinute(canvas, minute);
        //是否显示秒数
        if (showSeconds) {
            int second = calendar.get(Calendar.SECOND);
            Paint textPaint = getPaint(lineColor);
            textPaint.setTextSize(25f);
            //绘制秒数
            if (second < 10) {
                canvas.drawText("0" + second, lineWidth / 2 + padding / 2, lineWidth, textPaint);
            } else {
                canvas.drawText(second + "", lineWidth / 2 + padding / 2, lineWidth, textPaint);
            }
        }
        canvas.restore();
        //每隔一秒刷新一次
        postInvalidateDelayed(1000);
    }


    /**
     * 绘制 时
     *
     * @param canvas 画布
     * @param hour 时
     */
    public void drawHour(Canvas canvas, int hour) {
        int tenOfHour = hour / 10;//十位
        int oneOfHour = hour % 10;//个位
        //计算可得第一个数字的中心点 与 此时 canvas 的原点距离为 lineWidth+lineWidth/2 + padding + centerPadding / 2
        float newCenterX = -(lineWidth+lineWidth/2 + padding + centerPadding / 2);
        //将画布往左平移 newCenterX 为负值
        canvas.translate(newCenterX, 0);
        //开始绘制「时」中的十位
        numPaintUtil.drawNumber(tenOfHour);
        //计算可得第二个数字中心点与第一个数字的中心点(即此时 canvas 的原点)距离为lineWidth + padding ,为正值
        //所以将画布在上一次偏移量的基础上再往右平移 lineWidth + padding
        canvas.translate(lineWidth + padding, 0);
        //绘制「时」中的个位
        numPaintUtil.drawNumber(oneOfHour);
    }

    /**
     * 绘制时与分之间闪烁的两个小点
     *
     * @param canvas
     */
    public void drawPointBtHourNMinuter(Canvas canvas) {
        Paint pointPaint = getPaint(pointColor);
        //计算可得其坐标值
        canvas.drawCircle(lineWidth / 2 + centerPadding / 2, lineWidth / 2, 5, pointPaint);
        canvas.drawCircle(lineWidth / 2 + centerPadding / 2, -lineWidth / 2, 5, pointPaint);

    }

    /**
     * 绘制 分
     *
     * @param canvas 画布
     * @param minute 分钟
     */
    public void drawMinute(Canvas canvas, int minute) {
        int tenOfMinute = minute / 10;//十位
        int oneOfMinuter = minute % 10;//个位
        //第三个数字的中心点与第二个数字的中心点(即此时 canvas 的原点)的距离 为lineWidth + centerPadding,为正值
        //将画布往右平移 lineWidth + centerPadding
        canvas.translate(lineWidth + centerPadding, 0);
        //绘制分钟的十位
        numPaintUtil.drawNumber(tenOfMinute);
        //第四个数字的中心点与第三个数字的中心点(即此时 canvas 的原点)的距离为lineWidth + padding,为正值
        //将画布往右平移lineWidth + padding
        canvas.translate(lineWidth + padding, 0);
        //绘制分钟的个位
        numPaintUtil.drawNumber(oneOfMinuter);
    }
    
    /**
     * 获得对应颜色的画笔
     *
     * @param color 颜色
     * @return
     */
    public Paint getPaint(int color) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(color);
        paint.setStrokeWidth(5f);
        paint.setDither(true);
        return paint;
    }

源码请浏览https://github.com/yuwenque/ClockSample

你可能感兴趣的:(唔得闲都要搞事之实现LED数字时钟)