自定义数字时钟DigitalClockView

有这么一个需求:要求在应用中每个界面都有一个时钟显示时间,以秒更新。


自然而然,就想到了自定义View—以TextView+Handler方式倒是虽然可以实现,但难免累赘;
而时钟显示肯定要本身维护一个更新UI的线程,继承View固然是正确的,但不如继承SurfaceView来得更为直接—作为一个经常用来播放视频和展现游戏画面的控件,无疑是最佳选择。


很简单的东西,除了surfaceview的通常注意事项,额外注意三点即可:

  • 内容位置—行间距的处理
  • 适配处理—根据内容大小调整边界
  • 清屏刷新—最好使背景透明以便复用

代码如下:

public class DigitalClockView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    /**
     * 视图控制
     */
    private SurfaceHolder mHolder;
    /**
     * 画布
     */
    private Canvas mCanvas;
    /**
     * 线程开启或停止标志
     */
    private boolean isRun;

    /**
     * 日期画笔
     */
    private TextPaint mDatePaint;
    /**
     * 时间画笔
     */
    private TextPaint mTimePaint;

    /**
     * 日期格式
     */
    private String mDateFormat;
    /**
     * 时间格式
     */
    private String mTimeFormat;

    /**
     * 日期文字大小
     */
    private float mDateTextSize;
    /**
     * 时间文字大小
     */
    private float mTimeTextSize;

    /**
     * 日期文字内容距边界大小
     */
    private float mDateTopPadding;
    /**
     * 时间文字内容距日期文字内容大小
     */
    private float mTimeTopPadding;

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

    public DigitalClockView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DigitalClockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        int dateTextColor = -1;
        int timeTextColor = -1;
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DigitalClockView);
        final int count = a.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
            case R.styleable.DigitalClockView_date_format:
                mDateFormat = a.getString(attr);
                break;
            case R.styleable.DigitalClockView_time_format:
                mTimeFormat = a.getString(attr);
                break;
            case R.styleable.DigitalClockView_date_text_size:
                mDateTextSize = a.getDimension(attr, 20);
                break;
            case R.styleable.DigitalClockView_time_text_size:
                mTimeTextSize = a.getDimension(attr, 20);
                break;
            case R.styleable.DigitalClockView_date_text_color:
                dateTextColor = a.getColor(attr, -1);
                break;
            case R.styleable.DigitalClockView_time_text_color:
                timeTextColor = a.getColor(attr, -1);
                break;
            }
        }

        // 文字画笔
        mDatePaint = new TextPaint();
        mTimePaint = new TextPaint();
        // 画笔是否抗锯齿
        mDatePaint.setAntiAlias(true);
        mTimePaint.setAntiAlias(true);
        // 文字居中
        mDatePaint.setTextAlign(Paint.Align.CENTER);
        mTimePaint.setTextAlign(Paint.Align.CENTER);
        // 文字颜色
        mDatePaint.setColor(dateTextColor);
        mTimePaint.setColor(timeTextColor);
        // 文字大小
        mDatePaint.setTextSize(mDateTextSize);
        mTimePaint.setTextSize(mTimeTextSize);

        // 文字绘制位置
        Paint.FontMetrics fontMetrics1 = mDatePaint.getFontMetrics();
        Paint.FontMetrics fontMetrics2 = mTimePaint.getFontMetrics();
        mDateTopPadding = Math.abs(fontMetrics1.top);
        mTimeTopPadding = fontMetrics1.bottom - fontMetrics1.top + Math.abs(fontMetrics2.ascent);

        // 获取控制
        mHolder = getHolder();
        mHolder.addCallback(this);

        // 背景透明
        this.setZOrderOnTop(true);
        this.getHolder().setFormat(PixelFormat.TRANSPARENT);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isRun = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //一般不必覆写
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRun = false;
    }

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

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

        int desiredWidth, desiredHeight;
        if (widthMode == MeasureSpec.EXACTLY) {
            desiredWidth = widthSize;
        } else {
            // 以文字宽度为适配
            int dateTextWidth = (int) mDatePaint.measureText(mDateFormat);
            int timeTextWidth = (int) mTimePaint.measureText(mTimeFormat);
            desiredWidth = Math.max(dateTextWidth, timeTextWidth);
            if (widthMode == MeasureSpec.AT_MOST) {
                desiredWidth = Math.min(widthSize, desiredWidth);
            }
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            desiredHeight = heightSize;
        } else {
            // 以文字高度为适配
            Paint.FontMetrics fontMetrics1 = mDatePaint.getFontMetrics();
            Paint.FontMetrics fontMetrics2 = mTimePaint.getFontMetrics();
            desiredHeight = (int) (fontMetrics1.bottom - fontMetrics1.top + fontMetrics2.bottom - fontMetrics2.top);
            if (heightMode == MeasureSpec.AT_MOST) {
                desiredHeight = Math.min(heightSize, desiredHeight);
            }
        }
        setMeasuredDimension(desiredWidth, desiredHeight);
    }

    @Override
    public void run() {
        long start, end;
        while (isRun) {
            start = System.currentTimeMillis();
            draw();
            end = System.currentTimeMillis();

            // 消除延时
            try {
                if (end - start < 1000) {
                    Thread.sleep(1000 - (end - start));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            if (mCanvas != null) {
                // 刷屏,透明覆盖
                mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
                // 绘制文字
                mCanvas.drawText(SysUtil.getDate(System.currentTimeMillis(), mDateFormat), mCanvas.getWidth() / 2,
                        mDateTopPadding, mDatePaint);
                mCanvas.drawText(SysUtil.getDate(System.currentTimeMillis(), mTimeFormat), mCanvas.getWidth() / 2,
                        mTimeTopPadding, mTimePaint);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

自定义属性attrs.xml,包括了时间格式、字体大小、字体颜色三种属性,在代码中读取设置即可:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="DigitalClockView">
        <attr name="date_format" format="string|reference"/>
        <attr name="time_format" format="string|reference"/>
        <attr name="date_text_size" format="dimension|reference"/>
        <attr name="time_text_size" format="dimension|reference"/>
        <attr name="date_text_color" format="color|reference"/>
        <attr name="time_text_color" format="color|reference"/>
    </declare-styleable>

</resources>

最后是应用activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/darkviolet" >

    <com.xter.clock.view.DigitalClockView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        app:date_format="yyyy年MM月dd日"
        app:date_text_color="@color/white"
        app:date_text_size="@dimen/text_18"
        app:time_format="HH:mm:ss"
        app:time_text_color="@color/white"
        app:time_text_size="@dimen/text_34" />

</RelativeLayout>

效果图:
这里写图片描述
源码:

https://github.com/ifmylove2011/digital_clock.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值