美文网首页Android开发Android知识Android开发
唔得闲都要搞事之实现LED数字时钟

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

作者: Arestory | 来源:发表于2016-12-18 23:49 被阅读220次

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

    LED 数字时钟效果图

    image

    1. 如何实现LED数字

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

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

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


    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的特性绘制线条从而实现对应的数字<BR/>
         * 使用前,需初始化设置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 文件,增加以下属性,并获取样式

    
        <declare-styleable name="NumClock">
            <!-- 线条颜色 -->
            <attr name="lineColor" format="color"></attr>
            <!--是否显示秒数 -->
            <attr name="showSecond" format="boolean"></attr>
            <!-- 中间闪烁点的颜色-->
            <attr name="pointColor" format="color"></attr> 
        </declare-styleable>
    
     /**
         * 获取样式,假如出现异常时取默认值
         *
         * @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数字时钟

        本文链接:https://www.haomeiwen.com/subject/ttizmttx.html