让代码更简单

安卓自定义View仿小米定时器实现代码

重要:本文最后更新于2022-11-08 19:25:05,某些文章具有时效性,若有错误或已失效,请在下方留言或联系代码狗

今天在写APP的时候遇到一个需求,要显示事件发生了多久了,首先想到的是如本站评论一样,弄个标签,显示多久多久前。然而老板并不是这个意思,要弄个表一样的东西,让它转动,我擦….简直坑爹,于是我就发挥我面向百度编程的技能,找到各种定时器、时钟的开源项目,比如仿华为时钟的,仿小米时钟的。还有我接下来介绍的安卓仿小米定时器的自定义view实现代码。效果很不错,是我需要的样子,大家看看效果吧。

安卓自定义View仿小米定时器实现代码

安卓自定义View仿小米定时器实现代码

废话不多说,直接看实现代码!

复制
public class StopwatchView extends View {
    private final Paint mTrianglePaint;//三角形画笔
    private final Paint mLinePaint;//刻度线的画笔
    private final Paint mTextPaint;//文字画笔
    private final Paint mInnerCirclePaint;//内部圆形

    private int mLen; //实际尺寸大小
    private int mMilliseconds; //计时的总毫秒数
    private int outerAngle;//外圆指针的角度
    private int innerAngle;//小圆指针角度
    private float mTriangleLen;
    private boolean isPause;//是否暂停的开关
    float eachLineAngle = 360f / 240f; //两个刻度线之间的角度1.5° 共240条线 240间隔
    private Timer mTimer;   //定时器
    private String mShowContent; //显示总的时间

    public StopwatchView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //三角形指针画笔
        mTrianglePaint = new Paint();
        mTrianglePaint.setColor(Color.WHITE);
        mTrianglePaint.setAntiAlias(true); //抗锯齿
        //刻度线的画笔
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(2); //设线宽
        //文字画笔
        mTextPaint = new Paint();
        mTextPaint.setTextAlign(Paint.Align.CENTER); //文字居中
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStrokeWidth(2);
        //内部圆形画笔
        mInnerCirclePaint = new Paint();
        mInnerCirclePaint.setColor(Color.WHITE);
        mInnerCirclePaint.setStyle(Paint.Style.STROKE); //无填充
        mInnerCirclePaint.setAntiAlias(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //重新定义尺寸,保证为正方形
        int width = measuredDimension(widthMeasureSpec);
        int height = measuredDimension(heightMeasureSpec);
        mLen = Math.min(width, height);
        //小三角形指针端点到外圆之间的距离,用于计算三角形坐标[这里取整体宽度的1/16]
        mTriangleLen = (float) mLen / 16.0f;
        //提交设置新的值
        setMeasuredDimension(mLen, mLen);
    }

    //适配不同尺寸
    private int measuredDimension(int measureSpec) {
        int defaultSize = 800; //默认大小
        int mode = MeasureSpec.getMode(measureSpec); //宽高度设定方式
        int size = MeasureSpec.getSize(measureSpec); //宽高度测量大小
        switch (mode) {
            case MeasureSpec.EXACTLY: //尺寸指定
                return size;
            case MeasureSpec.AT_MOST: //match_parent
                return size;
            case MeasureSpec.UNSPECIFIED: //wrap_content
                return defaultSize;
            default:
                return defaultSize;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        calculateValue();
        drawTriangle(canvas);
        drawLine(canvas);
        drawText(canvas);
        drawSecondHand(canvas);
    }

    //计算相关值【根据当前毫秒值,计算外指针角度和内圆指针角度】
    private void calculateValue() {
        //文字
        int hours = mMilliseconds / (1000 * 60 * 60);
        int minutes = (mMilliseconds % (1000 * 60 * 60)) / (1000 * 60);
        int seconds = (mMilliseconds - hours * (1000 * 60 * 60) - minutes * (1000 * 60)) / 1000;
        int milliSec = mMilliseconds % 1000 / 100;
        if (hours == 0) {
            mShowContent = toDoubleDigit(minutes) + ":" + toDoubleDigit(seconds) + "." + milliSec;
        } else {
            mShowContent = toDoubleDigit(hours) + ":" + toDoubleDigit(minutes) + ":" + toDoubleDigit(seconds) + "." + milliSec;
        }

        //外角度
        outerAngle = 360 * (mMilliseconds % 60000) / 60000;
        //内角度
        innerAngle = 360 * (mMilliseconds % 1000) / 1000;
    }

    //根据角度绘制内部秒针
    private void drawSecondHand(Canvas canvas) {
        canvas.save();
        canvas.translate(mLen / 2, (float) mLen * 3 / 4.0f - mLen / 16);
        canvas.drawCircle(0, 0, mLen / 12, mInnerCirclePaint);
        canvas.drawCircle(0, 0, mLen / 80, mInnerCirclePaint);
        canvas.rotate(innerAngle);
        canvas.drawLine(0, mLen / 80, 0, mLen / 14, mInnerCirclePaint);
        canvas.restore();
    }

    //绘制文字
    private void drawText(Canvas canvas) {
        canvas.save();
        canvas.translate(mLen / 2, mLen / 2);
        mTextPaint.setTextSize(mLen / 10);
        canvas.drawText(mShowContent, 0, 0, mTextPaint);
        canvas.restore();
    }

    //绘制外部刻度线
    private void drawLine(Canvas canvas) {
        canvas.save();
        canvas.translate(mLen / 2, mLen / 2);
        int totalLines = (int) (360f / eachLineAngle); //240条线
        int lastLine = (int) (outerAngle / eachLineAngle);  //最亮的线条
        int firstLine = lastLine - ((int) (90 / eachLineAngle)); //最暗的一条
        boolean negativeFlag = false; //负数标志【即表示跨过了0起始坐标】
        if (firstLine < 0) {
            negativeFlag = true;
            firstLine = totalLines - Math.abs(firstLine);
        }
        int count = 0;
        for (int i = 0; i < totalLines; i++) {
            canvas.rotate(eachLineAngle);
            int color = 0;
            if (!negativeFlag) {
                //没有跨过起始点标志
                if (i >= firstLine && i <= lastLine && count < (totalLines / 4)) {
                    count++;
                    color = Color.argb(255 - ((totalLines / 4 - count) * 3), 255, 255, 255);
                } else {
                    color = Color.argb(255 - (int) (360f * 3 / (eachLineAngle * 4)), 255, 255, 255);
                }
            } else {
                //跨过起始点
                if (i >= 0 && i < lastLine) {
                    if (count == 0) {
                        count = totalLines / 4 - lastLine;
                    } else {
                        count++;
                    }
                    color = Color.argb(255 - ((totalLines / 4 - count) * 3), 255, 255, 255);
                } else if (mMilliseconds != 0 && i < totalLines && i >= firstLine) {  //mMilliseconds!=0 条件限制,目的是初始化时 都是灰色线条
                    count++;
                    color = Color.argb(255 - ((totalLines / 4 - (i - firstLine)) * 3), 255, 255, 255);
                } else {
                    color = Color.argb(255 - (int) (360f * 3 / (eachLineAngle * 4)), 255, 255, 255);
                }
            }
            mLinePaint.setColor(color);
            //mTriangleLen/5距离 目的是为了三角形到线条之间保留的距离
            canvas.drawLine(0, (float) (mLen / 2 - (mTriangleLen + mTriangleLen / 5)), 0, (float) (mLen / 2 - (2 * mTriangleLen + mTriangleLen / 5)), mLinePaint);

        }
        canvas.restore();
    }

    //根据角度绘制三角形
    private void drawTriangle(Canvas canvas) {
        canvas.save();
        //确定坐标
        canvas.translate(mLen / 2, mLen / 2);
        canvas.rotate(outerAngle);
        //画三角形
        Path p = new Path();
        //指针点
        p.moveTo(0, mLen / 2 - mTriangleLen);
        //左右侧点
        p.lineTo(0.5f * mTriangleLen, mLen / 2 - 0.134f * mTriangleLen);
        p.lineTo(-0.5f * mTriangleLen, mLen / 2 - 0.134f * mTriangleLen);
        p.close();
        canvas.drawPath(p, mTrianglePaint);
        canvas.restore();
    }

    //转成两位数 如传入1 返回01,
    private String toDoubleDigit(int value) {
        if (value < 10) {
            return "0" + value;
        } else {
            return "" + value;
        }
    }

    //开始
    public void start() {
        if (mTimer == null) {
            mTimer = new Timer();
            mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    if (!isPause) {
                        mMilliseconds += 50;
                        //工作线程中用postInvalidate(); UI线程用invalidate()
                        postInvalidate();
                    }
                }
            }, 50, 50);
        } else {
            resume();
        }
    }

    //暂停
    public void pause() {
        isPause = true;
    }

    //继续
    private void resume() {
        isPause = false;
    }

    //重置
    public void reset() {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
        isPause = false;
        mMilliseconds = 0;
        invalidate();
    }
public  void setmMilliseconds(String datetime){
    SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
    try {
        Date date=simpleDateFormat.parse(datetime);
        long startTime =fromDateStringToLong(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(new Date()));
        long stopTime = fromDateStringToLong(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(date));
        this.mMilliseconds=(int)(startTime-stopTime);
    } catch (ParseException e) {
        e.printStackTrace();
    }

}
    //记录
    public int record() {
        return mMilliseconds;
    }

    /**
     * 根据String型时间,获取long型时间,单位毫秒
     * @param inVal 时间字符串
     * @return long型时间
     */
    public static long fromDateStringToLong(String inVal) {
        Date date = null;
        SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
        try {
            date = inputFormat.parse(inVal);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return date.getTime();
    }
}

作者并没有提供设置已经过去多久的方法,是我自己添加的,效果还可以,没有什么问题,注意使用了毫秒级的日期时间格式化。

使用方法

新建一个java文件,将上面的代码保存下来,然后在布局文件中这样使用:

复制
<com.daimadog.org.myview.StopwatchView
    android:id="@+id/myclock"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

其中,com.daimadog.org.myview是我的安卓包名,也就是你新建java文件后出现在文件最上方的那一行package后面的字符串,使用时记得改成你自己的。

然后在activity中使用findViewById找到控件,根据你的需要使用start、stop、pause、resume、reset几个方法即可,另外上面我提供了一个初始化时间的方法,只需在start之前使用setmMilliseconds方法传入开始日期时间字符串即可!

感觉很棒!可以赞赏支持我哟~

2 打赏

评论 (1)

登录后评论
大佬牛逼,膜拜ing……
QQ咨询 邮件咨询 狗哥推荐