Android Studio实现简单的自定义钟表
项目目录
- 一、项目概述
- 二、开发环境
- 三、详细设计
- 3.1、尺寸设置
- 3.2、绘制表盘和指针
- 3.3、动态效果
- 四、运行演示
- 五、总结展望
- 六、源码获取
一、项目概述
在安卓开发中,当系统自带的View已经无法满足项目需求时,就要自定义View。在Android中是没有与钟表有关的View,因此我们制作一个简单的钟表View,这样就可以在其他项目中进行使用。
自定义钟表具有表盘,表盘上有12个刻度,有时针、分针、秒针,和家里面的石英表样式相同,用于显示时间会比数码表更加有内涵。
二、开发环境
只要是21年之后从Android Studio官网
下载的AS,都可以运行该App。因为高版本IDE向下兼容,只需要修改Java环境。
三、详细设计
3.1、尺寸设置
onMeasure
方法被重写用于决定自定义View的最终大小。这个过程考虑了父布局传递过来的宽度和高度的具体规格(spec)。MeasureSpec
类提供了一种方式来理解这些规格,包括它们的模式和大小。
模式有三种:
UNSPECIFIED
:父布局没有限制子View的大小,子View可以选择任何大小。EXACTLY
:父布局指定了一个确切的大小,子View应该尽可能地匹配这个大小。AT_MOST
:父Layout设定了一个最大值,子View的大小不能超过这个值。
在代码中,首先检查了宽度和高度的规格模式。
- 如果宽度和高度都是
EXACTLY
,则取两者中的较小值作为View的大小。 - 如果只有高度是
EXACTLY
,则取高度的值作为View的大小。 - 如果只有宽度是
EXACTLY
,则取宽度的值作为View的大小。 - 如果两者都不是
EXACTLY
,则取一个默认的值400作为View的大小。
最后,调用setMeasuredDimension(int, int)
方法来设置View的大小。这个方法接受两个参数:第一个是View的宽度,第二个是View的高度。由于在本例中,View是一个圆形,所以不管宽度还是高度,最终的大小都会被设置为相同的值,从而保证View是完美圆形的。
这种方法确保了View在不同设备和屏幕方向上具有一致的外观和大小,前提是父布局至少为View指定了一个方向上的确切大小。如果宽度和高度都没有具体的规格,那么View将会有一个默认的400px大小。这可能会导致View在布局中超出预期的范围,因此在实际应用中,可能需要对这种情况进行额外的处理。
//显示的尺寸,和使用时传入的宽高相关,因为整体为圆形@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取传入宽高的模式int wmode = MeasureSpec.getMode(widthMeasureSpec);int hmode = MeasureSpec.getMode(heightMeasureSpec);int wsize = MeasureSpec.getSize(widthMeasureSpec);int hsize = MeasureSpec.getSize(heightMeasureSpec);//判断模式,获取最终显示的尺寸int size = 400;if (wmode == MeasureSpec.EXACTLY) {if (hmode == MeasureSpec.EXACTLY) {size = Math.min(wsize, hsize);} else {size = wsize;}} else {if (hmode == MeasureSpec.EXACTLY) {size = hsize;} else {size = 400;}}//将测量好的值设置给宽高setMeasuredDimension(size, size);}
3.2、绘制表盘和指针
重写onDraw
方法来绘制时钟的表盘和指针。在绘制之前,先创建一个Paint
对象并根据需要设置其样式、颜色、宽度等属性。设置Paint对象的抗锯齿模式为true,这样可以让时钟的数字和指针看起来更加平滑。
Paint paint = new Paint();//设置抗锯齿paint.setAntiAlias(true);//获取在布局当中设置的自定义属性,设置给viewTypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColokView);int color = typedArray.getColor(R.styleable.ColokView_clockColor, Color.BLACK);//设置画笔的颜色paint.setColor(color);
时钟想要显示当前时间,所以必须获取系统的准确时间,并将其分解为小时、分钟和秒。定义一个getTime
方法获取系统时间。首先,创建了一个 Calendar
类的实例,通过调用静态方法 getInstance()
来初始化该实例,这样可以确保 calendar
对象包含了调用该方法时设备上的当前日期和时间。
接下来,使用 calendar
对象来获取当前的时间:
hours = calendar.get(Calendar.HOUR);
这行代码获取了当前的小时数,但是请注意,这是基于12小时制的,所以它返回的小时数范围是0(午夜12点)到11(中午12点)。minutes = calendar.get(Calendar.MINUTE);
这行代码获取了当前的分钟数,范围是0到59。seconds = calendar.get(Calendar.SECOND);
这行代码获取了当前的秒数,范围也是0到59。
//获取当前时间的方法public void getTime() {Calendar calendar = Calendar.getInstance();hours = calendar.get(Calendar.HOUR);minutes = calendar.get(Calendar.MINUTE);seconds = calendar.get(Calendar.SECOND);}
下面讲解onDraw
方法的具体实现,它负责在View上绘制表盘和指针。
- 首先,覆盖了
onDraw
方法,这个方法是View类的一部分,用于在View上进行绘制。 - 调用
super.onDraw(canvas);
确保父类的绘制逻辑得到执行。 - 设置画笔的风格为空心(
STROKE
),这样绘制的图形只有边缘有颜色。 - 设置View的内边距为20像素。
- 绘制外层大圈,设置线条宽度为8像素,以View中心为圆心,以View宽度的一半减去20像素为半径绘制一个圆。
- 绘制内层大圆,设置线条宽度为4像素,以View中心为圆心,以View宽度的一半减去30像素为半径绘制一个圆。
- 绘制时钟的中心点小圆,设置填充样式为
FILL
,以View中心为圆心,以10像素为半径绘制一个圆。 - 循环12次,绘制时钟的12个刻度。每次循环中:
- 保存当前的
Canvas
状态。 - 使用
canvas.rotate()
方法根据角度绘制刻度,这里有一个问题,因为每次旋转后都应该绘制新的刻度,但代码中却重复绘制了相同的刻度,这可能是一个错误。 - 发送一个空消息延迟1秒(通过
handler.sendEmptyMessageDelayed(1, 1000);
),这部分代码的意图可能是让时钟每秒移动一次,但它被放置在了绘制刻度的循环中,这也是一个逻辑错误。
- 保存当前的
- 绘制时针:
- 设置画笔宽度为8像素。
- 保存
Canvas
状态。 - 根据当前小时数和分钟数计算出的角度旋转
Canvas
。 - 绘制时针,从View中心向上40像素处开始到60像素处结束。
- 恢复
Canvas
状态。
- 绘制分针:
- 设置画笔宽度为5像素。
- 保存
Canvas
状态。 - 根据当前分钟数计算出的角度旋转
Canvas
。 - 绘制分针,从View中心向上2/3的高度处开始向下2/3的高度处结束。
- 恢复
Canvas
状态。
- 绘制秒针与上述分针的逻辑基本相同,只是画笔宽度改为3像素,角度计算改为每秒钟6度。
//显示的内容就在onDraw方法中进行绘制@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//设置空心paint.setStyle(Paint.Style.STROKE);//设置内边距setPadding(20, 20, 20, 20);//绘制外层大圈paint.setStrokeWidth(8);//设置线条宽度canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - 20, paint);//绘制内层大圆paint.setStrokeWidth(4);canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - 30, paint);//绘制表中间轴心paint.setStyle(Paint.Style.FILL);canvas.drawCircle(getWidth() / 2, getHeight() / 2, 10, paint);//绘制表的刻度12个,通过旋转画布实现for (int i = 1; i <= 12; i++) {//保存画布的状态canvas.save();//旋转到指定的角度canvas.rotate(30 * i, getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, 40, getWidth() / 2, 60, paint);//恢复旋转之前的状态canvas.restore();handler.sendEmptyMessageDelayed(1, 1000);}//绘制时针,1h=30°,1m=0.5°paint.setStrokeWidth(8);canvas.save();//旋转画布,旋转的度数由当前时间决定canvas.rotate(30 * hours + 0.5f * minutes, getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - getHeight() / 5, paint);canvas.restore();//绘制分针,1min=6°paint.setStrokeWidth(5);canvas.save();canvas.rotate(6 * minutes, getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - getHeight() / 4, paint);canvas.restore();//绘制秒针,1s=6°paint.setStrokeWidth(3);canvas.save();canvas.rotate(6 * seconds, getWidth() / 2, getHeight() / 2);canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - getHeight() / 3, paint);canvas.restore();}
3.3、动态效果
为了让指针走起来,我们定义了一个Handler
的匿名子类,并覆写了其handleMessage
方法。Handler
通常用于处理线程间通信,特别是在Android开发中,它常用来处理UI线程(主线程)和后台线程之间的消息传递。
在handleMessage
方法中,首先检查传入的Message
对象的what
字段是否等于1。如果是,表示该消息需要被处理:
-
调用
getTime()
方法来获取当前的时间。 -
调用
invalidate()
方法强制重新绘制View。invalidate()
方法会告诉系统该View的部分或全部区域已经变得不再有效,需要重绘。调用此方法后,系统将安排onDraw
方法在适当的时候被再次调用。 -
使用
handler.sendEmptyMessageDelayed(1, 1000);
来设定一个定时器。这行代码的意思是,它会让当前的消息处理器(handler
)在1000毫秒(即1秒)后,再次向自己发送一个what
值为1的空Message
对象。这样就创建了一个每秒重复执行一次的循环,用于不断更新时钟时间并刷新界面。
通过Handler
在Android的主线程上每秒更新并重绘时钟界面,从而模拟了一个动态的时钟效果。
Handler handler = new Handler() {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if (msg.what == 1) {//重新获取时间getTime();//重新绘制界面invalidate();handler.sendEmptyMessageDelayed(1, 1000);}}};
四、运行演示
五、总结展望
总的来说,本次自定义钟表项目可以学习到编程技巧,并且加深对Canvas API的理解。大家可以考虑添加一些定制选项,如不同的表盘样式、颜色或字体,以便用户可以根据自己的喜好来个性化他们的时钟;也可以考虑添加一些高级功能,如秒表、闹钟或世界时钟,以扩展应用程序的实用性。简单的项目掌握透了就不简单,期待大家在此基础上制作更多的创新钟表!
六、源码获取
♻️下面两种方式都可以获取源代码 |
---|
1️⃣ 点击直接下载 Android Studio 自定义钟表 |
2️⃣关注公众号《 萌新加油站 》,后台回复: 钟表 |
🚀这有你错过的精彩内容 |
---|
Android Studio实现文艺阅读App |
Android Studio实现志愿者系统 |
Android Studio实现多功能日记本 |
Android Studio实现推箱子小游戏 |
Android Studio实现五子棋小游戏 |
普劝青年烈士,黄卷名流,发觉悟之心,破色魔之障。芙蓉白面,须知带肉骷髅。美貌红妆,不过蒙衣漏厕。纵对如玉如花之貌,皆存若姊若母之心。未犯淫邪者,宜防失足。曾行恶事者,务劝回头。更祈展转流通,迭相化导。必使在在齐归觉路,人人共出迷津。——《欲海回狂》
相关文章:
Android Studio实现简单的自定义钟表
项目目录 一、项目概述二、开发环境三、详细设计3.1、尺寸设置3.2、绘制表盘和指针3.3、动态效果 四、运行演示五、总结展望六、源码获取 一、项目概述 在安卓开发中,当系统自带的View已经无法满足项目需求时,就要自定义View。在Android中是没有与钟表有…...
C语言 举例说明循环嵌套
今天 我们来说循环的嵌套 如果一个循环体内 又包含了另一个循环结构 我们称之为循环的嵌套 我们之前学的 While do-while for 都可以进行相互的嵌套 如下图 在 While 循环语句中再嵌套一个 While 循环语句 do-while 中嵌套 do-while for中嵌套 for 例如 我们做一个九九乘法…...
一、ESP32基础知识
1、乐鑫产品线 系列特点ESP8266无ESP32无ESP32-S2无ESP32-C3无ESP32-S3无ESP32-C2/ESP8684无ESP32-C6无ESP32-H2无 2、开发方式 2.1、ESP-IDF (1)面向专业开发者。乐鑫官方开发框架,专门为ESP32系列芯片设计。支持C/C语言,并提供一套完整的API&#…...
我希望未来10年,人工智能可以帮我解决这4件小事
生活在一线大城市的我,现在几乎整天被大数据、人工智能、机器学习、智慧生活的词汇环绕立体包围着,让我时刻感觉到,再过10年,我们五一假期真的可以摆脱现在擦肩接踵的旅游盛况了。但我其实要求倒是没这么高,我真心希望…...
使用jdbc方式操作ClickHouse
1、创建测试表,和插入测试数据 create table t_order01(id UInt32,sku_id String,total_amount Decimal(16,2),create_time Datetime ) engine MergeTreepartition by toYYYYMMDD(create_time)primary key (id)order by (id,sku_id);insert into t_order01 values …...
百面算法工程师 | 支持向量机——SVM
文章目录 15.1 SVM15.2 SVM原理15.3 SVM解决问题的类型15.4 核函数的作用以及特点15.5 核函数的表达式15.6 SVM为什么引入对偶问题15.7 SVM使用SGD及步骤15.8 为什么SVM对缺失数据敏感15.9 SVM怎么防止过拟合 欢迎大家订阅我的专栏一起学习共同进步 祝大家早日拿到offer&#x…...
关于YOLO8学习(一)环境搭建,官方检测模型部署到手机
一,环境的搭建 环境 win10 python 3.11 cmake pytorch pycharm 过程 首先安装好一个pycharm,这里就不一一叙述了。 其次,选择好一个python版本,是关键所在。有些YOLO的版本,并不支持很高的python版本,博主选用的是python3.11版本。经过实际的测试,这个版本比较合适。…...
3.10设计模式——Template Method 模版方法模式(行为型)
意图 定义一个操作中的算法骨架,而将一些步骤延迟到子类中,Template Method 使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。 结构 AbstractClass(抽象类)定义抽象的原语操作,具体的子类将重定…...
SQL 基础 | UNION 用法介绍
在SQL中,UNION操作符用于合并两个或多个SELECT语句的结果集,形成一个新的结果集。 使用UNION时,合并的结果集列数必须相同,并且列的数据类型也需要兼容。 默认情况下,UNION会去除重复的行,只保留唯一的行。…...
学习如何使用PyQt5实现notebook功能
百度搜索“pyqt5中notebook控件”,AI自动生成相应例子的代码。在 PyQt5 中,QTabWidget 类被用作 Notebook 控件。以下是一个简单的示例,展示如何创建一个带有两个标签的 Notebook 控件,并在每个标签中放置一些文本。 import sys f…...
Python氮氧甲烷乙烷乙烯丙烯气体和固体热力学模型计算
🎯要点 🎯固体和粒子:计算二态系统、简谐振子和爱因斯坦固体的内能和比热,比较爱因斯坦固体和德拜固体。模拟多个粒子的一维和二维随机游走,在数值上确认方差的线性趋势,模拟多个粒子的梯度下降࿰…...
2024-04-30 区块链-以太坊-相关文档
摘要: 2024-04-30 区块链-以太坊-文档 以太坊-相关文档: https://github.com/ethereum/go-ethereum https://geth.ethereum.org/ https://geth.ethereum.org/docs https://ethereum.org/zh/ 以太坊开发文档 | ethereum.org 以太坊开发文档_w3cschool 以太坊开发文档 基础主题 …...
你用过最好用的AI工具有哪些?
你用过最好用的AI工具有哪些? 人工智能(AI)工具正在逐渐成为我们日常生活中不可或缺的助手,它们通过提供智能化服务,极大地提升了我们的工作效率和生活质量。以下是一些广泛使用的AI工具和应用,以及它们所…...
Amine-PEG-Amine,956496-54-1在生物成像、生物传感器等领域具有广泛的应用
【试剂详情】 英文名称 Amine-PEG-Amine,NH2-PEG-NH2 中文名称 氨基-聚乙二醇-氨基,氨基PEG氨基, 双端氨基聚乙二醇 CAS号 956496-54-1 外观性状 由分子量决定,液体或者固体 分子量 0.4k,0.6k,1k&…...
为什么深度学习中减小泛化误差称为“正则化(Regularization)”
深度学习的一个重要方面是正则化(Regularization),Ian Goodfellow在《Deep Learning 》称正则化(Regularization)就是减小泛化误差。那么,为什么减小泛化误差称为正则化呢? 首先看正则化——Re…...
【Linux网络编程】2.套接字、网络字节序、IP地址转换函数
目录 网络套接字 网络字节序 网络字节序和主机字节序的转换 IP地址转换函数 inet_pton 参数af 参数src 参数dst 返回值 inet_ntop 参数af 参数src 参数dst 参数size 返回值 网络套接字 socket,一个文件描述符指向一个套接字,该套接字内部…...
代码签名证书的工作原理和申请流程
随着软件分发渠道的多样化和黑客攻击手段的不断升级,确保软件的真实性和完整性变得尤为重要。这正是代码签名证书(Code Signing Certificate)发挥关键作用的领域。本文将深入探讨代码签名证书的基础概念、工作原理、重要性以及申请和使用流程…...
Python中的yield
文章目录 1. Python中的yield1.1 一个简单的示例1.2 示例的每一步含义 2. yield 和return的区别2.1 一个简单的示例2.2 示例中每一步的含义 3. yield中的send()方法3.1 一个简单的示例3.2 示例中每一步的含义 4. yield中的throw()方法4.1 一个简单的示例4.2 示例中每一步的含义…...
【Linux】基于 Jenkins+shell 实现更新服务所需文件 -->两种方式:ssh/Ansible
👨🎓博主简介 🏅云计算领域优质创作者 🏅华为云开发者社区专家博主 🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支…...
5月4(信息差)
🎄 HDMI ARC国产双精度浮点dsp杜比数码7.1声道解码AC3/dts/AAC环绕声光纤、同轴、USB输入解码板KC33C 🌍 国铁集团回应高铁票价将上涨 https://finance.eastmoney.com/a/202405043066422773.html ✨ 源代码管理平台GitLab发布人工智能编程助手DuoCha…...
【Spring】1.Spring中IOC与DI全解析
本节将详细介绍Spring框架的两个核心概念:控制反转(IOC)和依赖注入(DI)。首先,我们会探讨IOC和DI的定义,实现原理,优点和缺点。然后,我们将介绍如何在Spring中使用IOC和D…...
vue2迁移到vue3,v-model的调整
项目从vue2迁移到vue3,v-model不能再使用了,需要如何调整? 下面只提示变化最小的迁移,不赘述vue2和vue3中的常规写法。 vue2迁移到vue3,往往不想去调整之前的代码,以下就使用改动较小的方案进行调整。 I…...
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
🌈个人主页:是店小二呀 🌈C语言笔记专栏:C语言笔记 🌈C笔记专栏: C笔记 🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 文章目录 一、字符函数1.1 字符分类函数1.1.1 islower1.1.2 isupper 1.…...
android 如何显示网络地址对应的图片
1.android 如何显示网络地址对应的图片 在Android中显示网络地址对应的图片,通常有多种方法可以实现。以下是几种常见的方法: 1. 使用ImageView和Picasso库 Picasso是一个强大的图片加载库,它可以方便地加载网络图片并显示到ImageView中。 …...
【项目学习01_2024.05.02_Day04】
学习笔记 4 课程分类查询4.1需求分析4.2 接口定义4.3 接口开发4.3.1 树型表查询4.3.2 开发Mapper 4 课程分类查询 4.1需求分析 有课程分类的需求 course_category课程分类表的结构 这张表是一个树型结构,通过父结点id将各元素组成一个树。 利用mybatis-plus-gen…...
Android SQLite中的UNIQUE constraint failed错误
报错信息: UNIQUE constraint failed SQLite中的UNIQUE constraint failed错误,表明尝试插入或更新数据库时出现了违反唯一性约束的情况。唯一性约束确保在特定列或列集合中的值在表中是唯一的,即不能有重复的值。当你尝试插入或更新已存在于…...
嘴尚绝卤味:传承经典,缔造美食新风尚
卤味,作为中国传统美食的代表之一,历经千年的传承与发展,早已成为无数食客餐桌上的宠儿。而在这个美食盛行的时代,嘴尚绝卤味凭借其独特的口感和精湛的工艺,成为卤味市场中的佼佼者,引领着卤味文化的新潮流…...
Docker 入门与实践:从零开始构建容器化应用环境
Docker 一、docker常用命令docker ps 格式化输出Linux设置命令别名 二、数据卷相关命令挂载到默认目录(/var/lib/docker)挂载到本地目录 三、自定义镜像Dockerfile构建镜像的命令 四、网络自定义网络 五、DockerCompose相关命令 一、docker常用命令 dock…...
C# 用户控件UserControl事件解绑资源释放
用户控件继承子 UserControl 。 现在有个业务需求在UserControl 所在的窗体关闭时解除事件HMouseDown绑定。 因没有相关的Close事件。后来本人想了一个办法在 ROICtlDesigner类的 Dispose 方法中执行相关的释放代码 比如解除事件绑定 释放资源 public partial class ROICt…...
QT-QTCreator环境配置
准备工作: 下载QT: 链接:https://pan.baidu.com/s/1prJcsC4DGqhKiXvLuPQFVA?pwd60b3 提取码:60b3下载WindowsKits: 链接:https://pan.baidu.com/s/1QNiS3HpbH5M5kXx5AhkqnQ?pwde2h8 提取码:e2h8安装的…...
建筑模板厂家联系方式/360搜索引擎优化
相信经常关注网站优化SEO的人都发现,google算法在改变,百度也是。以前的一些网站优化SEO方法在目前来说已经失效不管用了,或者没什么大影响了。首先我们分析一下,要想百度更新自己的站,首先要让百度爬到自己的站。但是…...
营销型网站建设费用/百度seo泛解析代发排名
手机屏幕失灵了怎么办呢?手机屏幕失灵的情况,相信有些朋友会遇到,那么要怎么解决呢?下文小编就为大家带来手机屏幕失灵的解决方法,有需要的朋友可以来下文了解下哦~手机屏幕失灵解决方法:方法一:1、关闭手机…...
短视频广告接单平台/关键词seo资源
http://3060674.blog.51cto.com/3050674/1590803 好久没写文章了,最近要来刷下存在感,近两年,运维自动化被炒的火的不行,行业趋势不可挡,现在企业招运维工程师都要求会一门开发语言。我们公司也不例外,由于…...
有哪些公司建设网站/会计培训班要多少钱一般要学多久
新手小小白报道!(。・∀・)ノ 最近开始做毕设,就把这里当作毕设日记本啦。 打算记录一下毕设中学到的知识以及遇到的零零碎碎的问题。 我的毕设题目是-----基于微博的大学生情感分析系统 如果有类似方向的小伙伴可以一起交流(不过估计也没…...
网站要怎么做的吗/seo网站关键词优化费用
前言 小田,你看能不能做个监控大屏实时查看促销活动销售额(GMV)?小朱,搞促销活动的时候能不能实时统计下网站的 PV/UV 啊?小鹏,我们现在搞促销活动能不能实时统计销量 Top5 啊?小李…...
网站建设打造学院/网络营销推广方法十种
基本背景 oracle 有 sequence trigger 希望在新增的时候获取到新增这条记录的 id ( oracle 通过 trigger 调取 sequence ) 20210203 搞了3个多小时,终于找到一篇我要的文章 https://www.cnblogs.com/tuxiaoer/p/12725777.html 一、实现主键自增 在oracle数据库中&…...