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

前言:实现完前篇文章中的荧光时钟后,突然想起大学期间做数字电路实验经常见到的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 176,319评论 25 709
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,684评论 4 61
  • 第二章,全息北京交通镜像 “好的,先生,我已接入所有数据接口及视频”手机影子已经打开了所有的镜像系统接口,瞬间在北...
    众心无相阅读 1,733评论 1 1
  • 东吴山上空,晴空万里。 “小少姬!小少姬!你慢点,小的快跟不上了!” “哪里来的这么多废话。木里我要是因为你被爹爹...
    huihui酱阅读 2,854评论 0 4
  • 《路过,这个世界教我的事》五 “那些飘荡的日子,那些泥泞的道路,偶尔被泪水染湮……然而有些路该继续走,有些相遇在未...
    Erina_li10阅读 1,873评论 1 3

友情链接更多精彩内容