一般计时类的自定义 View 都要考虑一个问题,那就是绘制是需要时间的,虽然很短,但是随着时间的推移,误差就会越来越大,我之前写过自定义 View 实现钟表功能的博客,当时是通过每秒钟获取一次系统时间,然后根据系统时间重新绘制来保证不会产生误差的,如果要实现秒表功能,这个方法明显不行,应为秒表是自己计时的,和系统时间没有关系,为了防止产生误差,我们可以每秒钟都执行一次动画,一秒钟过完之后,无论动画有没有执行完,强制将时间推进到一秒之后,由于一秒钟之内产生的误差非常小,几乎无法察觉,这样就能有效的防止随着时间的推移而产生较大的误差了,接下来就直接贴上自定义秒表的代码

attrs;

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="StopWatchView"><!--半径--><attr name="radius" format="dimension"/><!--外圆颜色--><attr name="circle_color" format="color"/><!--主刻度颜色--><attr name="main_scale_color" format="color"/><!--其他刻度颜色--><attr name="other_scale_color" format="color"/><!--最小刻度颜色--><attr name="third_scale_color" format="color"/><!--秒针针颜色--><attr name="big_pointer_color" format="color"/><!--毫秒针颜色--><attr name="small_pointer_color" format="color"/><!--小圆盘颜色--><attr name="small_dial_color" format="color"/></declare-styleable>
</resources>

StopWatchView:

public class StopWatchView extends View {private int mainScaleColor; //主刻度颜色private int otherScaleColor; //其他刻度颜色private int thirdScaleColor; //最小刻度颜色private int bigPointerColor; //秒针针颜色private int smallPointerColor; //毫秒针颜色private int smallDialColor; //小圆盘颜色private int circleColor; //外圆颜色private Paint paint; //画笔private Bitmap background; //背景图片private float viewRadius; //半径大小private float oneUnit; //一个单位长度private float bigPointerDegree; //秒针转过的角度private float smallPointerDegree; //毫秒针转过的角度private int count; //动画执行次数private ValueAnimator animator; //执行的属性动画public StopWatchView(Context context) {super(context, null);}public StopWatchView(Context context, AttributeSet attrs) {super(context, attrs);initView(context, attrs);initPaint();initAnimator();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//去长度和宽度的最小值作为尺寸,保证view是一个正方形int specSize = Math.min(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));setMeasuredDimension(specSize, specSize);if (viewRadius == - 1) //如果未设置半径,把半径设为view的长度的一半viewRadius = specSize / 2;oneUnit = (float) (viewRadius / 453.0); //设置一个单位的大小}@Overrideprotected void onDraw(Canvas canvas) {canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);drawBackground(canvas);drawBigScale(canvas);
//        drawNumber(canvas);canvas.save();canvas.translate(0, - oneUnit * 134);drawSmallDial(canvas);drawSmallPointer(canvas);canvas.restore();canvas.save();drawBigPointer(canvas);canvas.restore();}//初始化控件private void initView(Context context, AttributeSet attrs) {TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.StopWatchView);viewRadius = ta.getDimension(R.styleable.StopWatchView_radius, -1);circleColor = ta.getColor(R.styleable.StopWatchView_circle_color, Color.BLACK);mainScaleColor = ta.getColor(R.styleable.StopWatchView_main_scale_color, Color.BLACK);otherScaleColor = ta.getColor(R.styleable.StopWatchView_other_scale_color, Color.BLACK);thirdScaleColor = ta.getColor(R.styleable.StopWatchView_third_scale_color, Color.BLACK);bigPointerColor = ta.getColor(R.styleable.StopWatchView_big_pointer_color, Color.BLACK);smallDialColor = ta.getColor(R.styleable.StopWatchView_small_dial_color, Color.BLACK);smallPointerColor = ta.getColor(R.styleable.StopWatchView_small_pointer_color, Color.BLACK);ta.recycle();background = BitmapFactory.decodeResource(getResources(), R.drawable.stop_watch_background);bigPointerDegree = 0;smallPointerDegree = 0;count = 0;}//初始化画笔private void initPaint() {paint = new Paint();paint.setAntiAlias(true);}//初始化属性动画private void initAnimator() {animator = ValueAnimator.ofFloat(0, 360);//每秒钟毫秒针转一圈,正好是360度animator.setDuration(1000);animator.setInterpolator(new LinearInterpolator());//设置线性执行动画animator.setRepeatCount(ValueAnimator.INFINITE);//设置无限循环animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {//设置动画监听,获取当前动画的值,重新计算指针转过的角度,然后重绘smallPointerDegree = (float) animation.getAnimatedValue();bigPointerDegree = 6 * count + (float) animation.getAnimatedValue() / 60;invalidate();}});animator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {Log.d("TAG", "onAnimationStart");}@Overridepublic void onAnimationEnd(Animator animation) {Log.d("TAG", "onAnimationEnd");}@Overridepublic void onAnimationCancel(Animator animation) {Log.d("TAG", "onAnimationCancel");}@Overridepublic void onAnimationRepeat(Animator animation) {//重复执行的时候强制把时间推进一秒,如果达到60秒的话,说明秒针已经转过一圈了,//把动画执行次数重置为0smallPointerDegree = 0;bigPointerDegree = 6 * count;count++;if (count >= 60) {count = 0;}}});}//    计算宽度private int measureWidth(int measureSpec) {int result = 0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;viewRadius = - 1;} else {//若长度为wrap_content,如果未设置半径,把长度设为200,否则长度为半径的两倍if (specMode == MeasureSpec.AT_MOST) {if (viewRadius == - 1) {result = 200;} else {result = (int) (viewRadius * 2);}}}return result;}//    计算高度private int measureHeight(int measureSpec) {int result = 0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;viewRadius = - 1;} else {if (specMode == MeasureSpec.AT_MOST) {if (viewRadius == - 1) {result = 200;} else {result = (int) (viewRadius * 2);}}}return result;}//画背景private void drawBackground(Canvas canvas) {int radiusInt = (int) viewRadius;Rect rect = new Rect(- radiusInt, - radiusInt, radiusInt, radiusInt);canvas.drawBitmap(background, null, rect, null);paint.setStrokeWidth(4 * oneUnit);paint.setColor(circleColor);paint.setStyle(Paint.Style.STROKE);canvas.drawCircle(0, 0, oneUnit * 261, paint);}//画大刻度private void drawBigScale(Canvas canvas) {paint.setColor(mainScaleColor);paint.setStyle(Paint.Style.FILL);paint.setStrokeWidth(oneUnit * 3);for (int i = 0; i < 12; i++) {canvas.drawLine(0, - oneUnit * 254, 0, - oneUnit * 238, paint);canvas.rotate(30);}paint.setColor(otherScaleColor);paint.setStrokeWidth(oneUnit * 2);for (int i = 0; i < 60; i++) {if (i % 5 != 0) {canvas.drawLine(0, - oneUnit * 254, 0, - oneUnit * 244, paint);}canvas.rotate(6);}paint.setColor(thirdScaleColor);for (int i = 0; i < 300; i++) {if (i % 5 != 0) {canvas.drawLine(0, - oneUnit * 254, 0, - oneUnit * 248, paint);}canvas.rotate((float) 1.2);}}/*    private void drawNumber(Canvas canvas) {paint.setColor(mainScaleColor);paint.setTextSize(oneUnit * 20);for (int i = 0; i < 12; i++) {canvas.drawText(5 * i + "", (float) (Math.sin(i * Math.PI / 6) * oneUnit * 220), (float) (- Math.cos(i * Math.PI / 6) * oneUnit * 220), paint);
//            canvas.rotate(30);}}*///画小圆盘和圆盘上的刻度private void drawSmallDial(Canvas canvas) {paint.setColor(smallDialColor);paint.setStrokeWidth(oneUnit * 2);paint.setStyle(Paint.Style.STROKE);canvas.drawCircle(0, 0, oneUnit * 52, paint);paint.setStyle(Paint.Style.FILL);paint.setStrokeWidth(oneUnit);for (int i = 0; i < 24; i++) {canvas.drawLine(0, - oneUnit * 51, 0, - oneUnit * 46, paint);canvas.rotate(15);}}//画秒针private void drawBigPointer(Canvas canvas) {paint.setColor(bigPointerColor);paint.setStrokeWidth(0);canvas.drawCircle(0, 0, (float) (oneUnit * 9.5), paint);Path path = new Path();path.moveTo((float) (- oneUnit * 4.5), oneUnit * 34);path.lineTo((float) (oneUnit * 4.5), oneUnit * 34);path.lineTo((float) (oneUnit * 2.5), oneUnit * - 254);path.lineTo((float) (- oneUnit * 2.5), oneUnit * - 254);path.close();canvas.rotate(bigPointerDegree);canvas.drawPath(path, paint);}//画毫秒针private void drawSmallPointer(Canvas canvas) {paint.setColor(smallPointerColor);canvas.drawCircle(0, 0, oneUnit * 5, paint);Path path = new Path();path.moveTo((float) (- oneUnit * 2.5), 0);path.lineTo((float) (oneUnit * 2.5), 0);path.lineTo(0, - oneUnit * 46);path.close();canvas.rotate(smallPointerDegree);canvas.drawPath(path, paint);}//开始计时public void start() {if (animator != null && !animator.isStarted())animator.start();}//暂停计时public long pause() {if (animator != null && animator.isRunning()) {long playTime = animator.getCurrentPlayTime();animator.cancel();return playTime;}return 0;}//暂停后重新计时public void restart(long playTime) {if (animator != null && !animator.isRunning()) {animator.setCurrentPlayTime(playTime);animator.start();}}//重置秒表状态public void clean() {if (animator != null && !animator.isStarted() && !animator.isRunning()) {animator.end();count = 0;bigPointerDegree = 0;smallPointerDegree = 0;}}
}

注释写得很清楚了,这里不再过多解释

自定义 View 实现秒表功能相关推荐

  1. android xml画圆,Android自定义View画圆功能

    本文实例为大家分享了Android自定义View画圆的具体代码,供大家参考,具体内容如下 引入布局 xmlns:tools="http://schemas.android.com/tools ...

  2. 自定义view实现涂鸦(画板)功能

    自定义view实现涂鸦功能,包括撤销.恢复.重做.保存以及橡皮擦(在风格中实现)功能,小模块包括画笔颜色调整.画笔尺寸调整.画笔类型(包括正常画笔以及橡皮擦功能),之后又陆续实现了画圆.画矩形以及画箭 ...

  3. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  4. android自定义起止时间的时间刻度尺,Android 自定义View篇(六)实现时钟表盘效果...

    前言 Android 自定义 View 是高级进阶不可或缺的内容,日常工作中,经常会遇到产品.UI 设计出花里胡哨的界面.当系统自带的控件不能满足开发需求时,就只能自己动手撸一个效果. 本文就带自定义 ...

  5. android代码实现手机加速功能,Android自定义View实现内存清理加速球效果

    Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...

  6. 自定义 View 功能上线,你的小程序可以更多变

    简介:基于自定义 View 能力,打造 App 多元化交互 你的自带控件,还够用吗? 对于 mPaaS 小程序应用开发者而言,mPaaS 中自带的控件并不陌生. 比如常用的 TextView.Edit ...

  7. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能

    支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能. 效果如下所示: 思路: 就是几个圆的半径不断在变大,这个可以使用动画缩放实现,还有透明动画 还有就是这是好几 ...

  8. android 自定义 对号,Android自定义View实现打钩动画功能

    先上效果图 动图 静态图 1. 回顾 [Android自定义View:一个精致的打钩小动画]上一篇文章,我们已经实现了基本上实现了控件的效果了,但是...但是...过了三四天后,仔细看回自己写的代码, ...

  9. 自定义 View(一)仿 QQ 列表 Item 侧拉删除功能

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

  10. android 开发打赏布局,Android自定义View模仿虎扑直播界面的打赏按钮功能

    Android自定义View模仿虎扑直播界面的打赏按钮功能 发布时间:2020-09-28 12:15:53 来源:脚本之家 阅读:77 作者:shenhuniurou 前言 作为一个资深篮球爱好者, ...

最新文章

  1. python画图库哪个好_机器学习基础5--python画图库matplotlib(上)
  2. 中国水平电泳系统市场趋势报告、技术动态创新及市场预测
  3. Redis 基本数据类型
  4. hdu-1695 GCD(莫比乌斯反演)
  5. 有多大的大脑互联网正在开发社交网络
  6. Maven仓库—Nexus环境搭建及使用
  7. RabbitMQ中Confirm确认与Return返回消息详解(八)
  8. Spring 揭密——第 1 章 Spring 框架的由来
  9. pdfboss转换器提供在线pdf转换word免费的api接口
  10. 新人主播开播以后,碰到的各类问题和解决方法
  11. 机械制造技术类毕业论文文献都有哪些?
  12. Android加载超大图片
  13. arm nodejs_nodejs是如何和libuv以及v8一起合作的?(文末有彩蛋哦)
  14. 基于pg_qualstats和hypopg的自动索引调优
  15. 一文看懂中国清算体系
  16. python将多个列表合并_Python中多个列表与字典的合并方法
  17. 不用管别人怎么评论自己_如何面对别人对自己的评价?
  18. #《算法竞赛入门经典》勘误
  19. c语言程序设计 doc,C语言程序设计61844.doc
  20. vs2017python如何打包exe_VS2017项目程序打包成.msi或者.exe

热门文章

  1. 小编辑的自我成长之“出版基础知识”
  2. html5与css3网页设计论文,HTML5与CSS3对网页设计制作工作的好处
  3. Google重返中国?并不是你想要的那个
  4. 七年级上册知识计算机计算,七年级数学上册知识点汇总
  5. 比UBUNTU还帅,还好用哦~~国内发行版Linux Deepin出新版啦!!!
  6. 树莓派wiringPI无root权限调用GPIO口
  7. 3g安卓市场_如果不是乔布斯,安卓差一点就步了黑莓后尘
  8. 微软绑定office攻占云服务市场
  9. [附源码]Nodejs计算机毕业设计化妆品销售网站Express(程序+LW)
  10. 浅议实名买火车票的不可行性