Android 自定义时钟控件 时针、分针、秒针的绘制这一篇就够了
前言
对于 Android 开发者来说,自定义 View 是绕不开的一个坎。二对一自定义 View 自定义时钟必然是首选,那么我们该如何绘制自定义时钟呢?本篇我结合 github 上一个有趣的三方库,来给大家讲讲如何作出我们的第一个时钟
现在开始:
目录
前言
现在开始:
前期准备:
重写 onMessure() 方法
配置 xml 文件
开始搭建之旅
获取当前时间
绘制秒针
定义画笔和颜色
定义长度值和 Path
绘制秒针
绘制分针
绘制时针
最后运行效果
前期准备:
对于所有的自定义 View 来说,构造方法、onMeasure(),onDraw() 这几个方法都是必不可少的。,所以哦你们先打出这套模版
public class ClockView extends View {public ClockView(Context context) {super(context);}public ClockView(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);}
}
重写 onMessure() 方法
重写 onMeasure() 方法的本质在于配置控件大小,而配置控件大小的重点就在于配置 setMeasuredDimension(..., ...) 方法。关于具体的配置细节可以参照:点击查看 https://blog.csdn.net/qq_43377749/article/details/91045764 这里以为是自定义时钟控件,所以内容很简单,在三种模式下分别放回三种值即可:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(measureDimension(widthMeasureSpec), measureDimension(heightMeasureSpec));}private int measureDimension(int measureSpec) {int defaultSize = 800;int model = MeasureSpec.getMode(measureSpec);int size = MeasureSpec.getSize(measureSpec);switch (model) {case MeasureSpec.EXACTLY:return size;case MeasureSpec.AT_MOST:return Math.min(size, defaultSize);case MeasureSpec.UNSPECIFIED:return defaultSize;default:return defaultSize;}}
配置 xml 文件
因为是自定义控件,所以逼着在这里自定义了一个控件属性文件,位于 /res/values/attr.xml 具体内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="ClockView"><attr name="clock_backgroundColor" format="color" /><attr name="clock_lightColor" format="color" /><attr name="clock_darkColor" format="color" /><attr name="clock_textSize" format="dimension" /></declare-styleable></resources>
开始搭建之旅
现在让我们开始搭建时钟,由于是时钟的搭建,所以我们基本可以分为一下三个步骤:
- 获得当前系统时间
- 绘制时针
- 绘制分针
- 绘制秒针
获取当前时间
首先,要绘制时钟,必然要获得当前的时间,要不三根指针非重合在一起不可,所以让我们先来研究下如何获得当前系统时间,这里我们就需要使用到一个叫做 Calendar 的工具类,Calendar 是 Android 开发中需要获取时间时必不可少的一个工具类。
所需要的信息基本可以分为
- milliSecond (毫秒,保证秒针滚动平滑不是一跳一跳)
- second
- minute
- hour
private void getCurrentTime() {Calendar calendar = Calendar.getInstance();float milliSecond = calendar.get(Calendar.MILLISECOND);float second = calendar.get(Calendar.SECOND) + milliSecond / 1000;// 精确到小数点后 保证圆滑float minute = calendar.get(Calendar.MINUTE) + second / 60;float hour = calendar.get(Calendar.HOUR) + minute / 60;}
但是这里有个问题,自定义 View 中,绘制时是根据某个倾斜角度进行绘制的,而非给系统一个浮点型的时间,他就会自动取绘制。所以这里我们还需要知道 每个时间(分秒时,占总时间的比重所代表的偏转角),在这里我们这三个全局私有变量:
/* 时针角度 */private float mHourDegree;/* 分针角度 */private float mMinuteDegree;/* 秒针角度 */private float mSecondDegree; private void getCurrentTime(){Calendar calendar = Calendar.getInstance();float milliSecond = calendar.get(Calendar.MILLISECOND);float second = calendar.get(Calendar.SECOND) + milliSecond / 1000;float minute = calendar.get(Calendar.MINUTE) + second / 60;float hour = calendar.get(Calendar.HOUR) + minute / 60;mSecondDegree = second / 60 * 360;mMinuteDegree = minute / 60 * 360;mHourDegree = hour / 60 * 360;}
最后别忘了在 onDraw() 中调用
绘制秒针
为了区别于时针分针单一的矩形,这里的秒针我们用一个三角尖代替:
- 既然是绘制图像,自定义画笔就是必不可少的
- 然后,画笔是用于上色的,所以我们还需要一个 Path 类对象将这个小三角的边界画出来
- 由于绘制是在成员方法中进行,所以我们需要定一个 Canvas 对象,来保存 onDraw() 中由于绘制视图的 Canvas
- 除此之外,秒针是有长度的,所以我们需要一个整型长度变量
- 最后,我们还需要一个整型变量来存储颜色值,颜色值应该从我们先前定义的 xml 文件的属性中获取。
定义画笔和颜色
由于时间是需要反复更新的,所以 onDraw() 方法也是要被反复调用的。这就使得 Paint 等变量不能再其中定义,而需要在构造方法中定义,否则难免有内存溢出的风险。
/* 亮色,用于分针、秒针、渐变终止色 */private int mLightColor;/* 秒针画笔 */private Paint mSecondHandPaint;public ClockView(Context context, AttributeSet attrs) {super(context, attrs);TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ClockView, 0, 0);mLightColor = ta.getColor(R.styleable.ClockView_clock_lightColor, Color.parseColor("#ffffff"));ta.recycle();mSecondHandPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mSecondHandPaint.setStyle(Paint.Style.FILL);mSecondHandPaint.setColor(mLightColor);}
定义长度值和 Path
长度值和 Path 的定义和 Paint 一样,不适合在 onDraw() 中,建议大家在 onSizeChanged 中定义,这个方法的提供了测量长度的各个形参。
/* 加一个默认的padding值,为了防止用camera旋转时钟时造成四周超出view大小 */private float mDefaultPadding;private float mPaddingTop;/* 时钟半径,不包括padding值 */private float mRadius;@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mRadius = Math.min(w - getPaddingLeft() - getPaddingRight(),h - getPaddingTop() - getPaddingBottom()) / 2;// 各个指针长度mDefaultPadding = 0.12f * mRadius;mPaddingTop = mDefaultPadding + h / 2 - mRadius + getPaddingTop();// 钟离上边界距离}
绘制秒针
这里把绘制方法命名为:drawSecondNeedle() 首先我们需要获得 Canvas 参数
/* 秒针路径 */private Path mSecondHandPath = new Path();@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mCanvas = canvas;getCurrentTime();drawSecondNeedle();invalidate();}
根据这个参数我们开始绘制秒针:
- 首先绘制画笔的 Style 设为 FILL 填充
- 定义一个 Path 对象由于绘制
- 调用 Path 对象的 moveTo 方法设定绘制起点
- 调用 lineTo 方法,绘制线条
- 调用 Canvas 的 close 方法将终点与起点连线形成封闭图形
private void drawSecondNeedle() {mCanvas.save();// ❑ save:用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。mCanvas.rotate(mSecondDegree, getWidth() / 2, getHeight() / 2);// 设置指针位置mSecondHandPath.reset();float offset = mPaddingTop;mSecondHandPath.moveTo(getWidth() / 2, offset + 0.26f * mRadius);// 这三行绘制三角尖mSecondHandPath.lineTo(getWidth() / 2 - 0.05f * mRadius, offset + 0.34f * mRadius);mSecondHandPath.lineTo(getWidth() / 2 + 0.05f * mRadius, offset + 0.34f * mRadius);mSecondHandPath.close();mSecondHandPaint.setColor(mLightColor);mCanvas.drawPath(mSecondHandPath, mSecondHandPaint);mCanvas.restore();// ❑ restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。}
绘制分针
要绘制分针首先得有以下准备
- 一个用于绘制分针的 Path 对象
- 一个用于绘制中心轴圆圈的 RectF 对象
- 一个用于画笔对象
/* 分针路径 */private Path mMinuteHandPath = new Path();/* 分针画笔 */private Paint mMinuteHandPaint;/* 小时圆圈的外接矩形 */private RectF mCircleRectF = new RectF();/*** 绘制分针*/private void drawMinuteNeedle() {mCanvas.save();mCanvas.rotate(mMinuteDegree, getWidth() / 2, getHeight() / 2);mMinuteHandPath.reset();float offset = mPaddingTop ;mMinuteHandPath.moveTo(getWidth() / 2 - 0.01f * mRadius, getHeight() / 2 - 0.03f * mRadius);mMinuteHandPath.lineTo(getWidth() / 2 - 0.008f * mRadius, offset + 0.365f * mRadius);mMinuteHandPath.quadTo(getWidth() / 2, offset + 0.345f * mRadius,getWidth() / 2 + 0.008f * mRadius, offset + 0.365f * mRadius);mMinuteHandPath.lineTo(getWidth() / 2 + 0.01f * mRadius, getHeight() / 2 - 0.03f * mRadius);mMinuteHandPath.close();mMinuteHandPaint.setStyle(Paint.Style.FILL);mCanvas.drawPath(mMinuteHandPath, mMinuteHandPaint);mCircleRectF.set(getWidth() / 2 - 0.03f * mRadius, getHeight() / 2 - 0.03f * mRadius,//绘制指针轴的小圆圈getWidth() / 2 + 0.03f * mRadius, getHeight() / 2 + 0.03f * mRadius);mMinuteHandPaint.setStyle(Paint.Style.STROKE);mMinuteHandPaint.setStrokeWidth(0.02f * mRadius);mCanvas.drawArc(mCircleRectF, 0, 360, false, mMinuteHandPaint);mCanvas.restore();}
- 首先我们根据之前计算获得的角度旋转画笔到当前要绘制的时间
- 然后我们绘制分针,绘制方法很简单,首先我们将画笔移到 View 中心篇左的地方
- 然后用 lineTo 绘制一条直线
- 接着用 quadTo 绘制一条曲线到右边对称点
- 再接着 用 lineTo 绘制一条直线到中心篇右
- 最后调用 close 方法闭合图形即可
至于绘制圆心轴的方法就不说了 就是最基本的绘制圆的方法,先设定 RectF 对象,在调用 fraeArc 方法绘制即可
绘制时针
绘制是真的过程与绘制分针一模一样,由于轴心圆的 RectF 可以直接调用之前绘制分针用到的,所以甚至是更简单些
/* 时针路径 */private Path mHourHandPath = new Path();/* 时针画笔 */private Paint mHourHandPaint;/*** 绘制时针*/private void drawHourHand() {mCanvas.save();mCanvas.rotate(mHourDegree, getWidth() / 2, getHeight() / 2);mHourHandPath.reset();float offset = mPaddingTop;mHourHandPath.moveTo(getWidth() / 2 - 0.018f * mRadius, getHeight() / 2 - 0.03f * mRadius);mHourHandPath.lineTo(getWidth() / 2 - 0.009f * mRadius, offset + 0.48f * mRadius);mHourHandPath.quadTo(getWidth() / 2, offset + 0.46f * mRadius,getWidth() / 2 + 0.009f * mRadius, offset + 0.48f * mRadius);mHourHandPath.lineTo(getWidth() / 2 + 0.018f * mRadius, getHeight() / 2 - 0.03f * mRadius);mHourHandPath.close();mHourHandPaint.setStyle(Paint.Style.FILL);mCanvas.drawPath(mHourHandPath, mHourHandPaint);mCircleRectF.set(getWidth() / 2 - 0.03f * mRadius, getHeight() / 2 - 0.03f * mRadius,getWidth() / 2 + 0.03f * mRadius, getHeight() / 2 + 0.03f * mRadius);mHourHandPaint.setStyle(Paint.Style.STROKE);mHourHandPaint.setStrokeWidth(0.01f * mRadius);mCanvas.drawArc(mCircleRectF, 0, 360, false, mHourHandPaint);mCanvas.restore();}
}
最后运行效果
让我们优化下界面
到此为止我们的小时钟就定义完啦,如果大家阅读过程中发现错误,欢迎评论区中指出呦~~
Android 自定义时钟控件 时针、分针、秒针的绘制这一篇就够了相关推荐
- Android自定义时钟控件
项目要求访问网络是等待状态要做时钟的样子,经过不懈努力,终于写出来了,现分享出来,功能比较全,直接拷贝代码就可以用,仅供有这种需求的码农们参考,如果采纳,请点个赞,谢谢支持. 效果图 主Activit ...
- 逻辑思维题之时针分针秒针问题
#时针分针秒针是否重合好像是高中物理题?# 如题: 一天有24小时,在这24小时之中,时钟的时针.分针重合多少次? 答:22次.24小时之内分针会转过24圈,而时针也会转过2圈(易忽略导致以为重合24 ...
- 时针分针秒针一天重合几次
假设时针的角速度是ω(ω=π/6每小时),则分针的角速度为12ω,秒针的角速度为720ω. 分针与时针再次重合的时间为t,则有12ωt-ωt=2πn 时 分 秒 1 60 3600 30 360 21 ...
- 计算时针分针秒针夹角的方法
将当前时间转换为秒(hour*60*60+minute*60+second)再除以表盘总秒数(12*60*60)再乘以360度就等于当前时针的度数. 分针,秒针同理. 再将时针度数分针度数做减法取绝对 ...
- 自定义Android电子时钟控件
参照Google的闹钟源码,提取出来其中的一个部分,获取系统的当前时间,并实时进行更新显示,写成一个控件view,以后有需要的话可以放在自己的App中,当做一个通用的控件来使用.本例子中只是简单的把源 ...
- Java计算12小时之内,时针分针秒针重合的次数
package demo;public class Test3 {public static void main(String[] args) {for (int s = 0; s < 60;) ...
- java时针_Java 设计一个clock类 要求实现时针,分针,秒针
展开全部 /* * @(#)Clock.java 1.16 06/02/22 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Rese ...
- 时针分针一天到底重合多少次?
在面试之中,除了技术面试以了解候选人的技术水平,有时还会有一些类似于脑筋急转弯的趣味题,来考察候选人的反应能力和思维能力等.今天我们就来分析一下下面这个问题: 时针分针一天重合多少次? 这显然是一个追 ...
- Android Studio利用时钟控件AnalogClock显示模拟时钟以及TextClock显示数字时钟
前言 利用时钟控件AnalogClock快速制作一个模拟时钟.利用TextClock显示数字时钟. 一.AnalogClock是什么? AnalogClock继承的是View,可重写OnDraw方法. ...
最新文章
- 多快好省的宏基因组研究技巧 — 资深专家分享
- R字符串(Strings)转为日期类型(Dates)
- IEnumerable和IQueryable在使用时的区别
- docker安装的环境
- android高德地图自定义图层,自定义图层-Canvas
- auto static 的区别
- (组合数学笔记)Pólya计数理论_Part.2_群及其性质
- 马斯克:如果我不担任CEO 特斯拉就会完蛋
- MS CRM 2011实施指南5.20版已经发布
- C++中的左值和右值的区别
- 黑苹果内置硬盘识别成外置硬盘_空间大?安全更重要,麦沃 K35274D硬盘阵列盒使用体验--数据无价...
- 物联网通信-期末复习
- 网页页面缩小放大的快捷键
- BT5 CDLinux+U盘启动 破解无线网络
- 相机光学(一)——成像系统分辨率的理论
- 商标设计需要注意的要素
- [力扣c语言实现]207. 课程表(拓扑排序)
- 常见的电脑蓝屏代码和解决方法
- 微淘百课微信万群直播定制机好用么?
- 用python做flash动画_将Flash应用于Python项目
热门文章
- Parameter - Out Of Values报错解决方法
- 盘点哪些平台可以做下拉词优化
- String a = “abc“和 String a = new String(“abc“) 的区别
- 怎么发外链才有用,利用外链提升网站排名的技巧!
- 【jzoj2194】【字符串】censor
- 数值计算方法与c语言工程函数库 pdf,数值计算方法和算法.PDF
- MUSH中的miniwindow(一 )
- 英特尔寻求分拆上市,Mobileye或“止步”汽车芯片巨头之战
- 查立得快搜系统(php xls) v1.0
- Eclipse几个版本号的区别