当前位置: 首页 > news >正文

HarmonyOS ArkTS 实战:实现一个精准秒表应用

HarmonyOS ArkTS 实战:实现一个精准秒表应用

项目效果

本文实现的是一个基于 HarmonyOS 和 ArkTS 的精准秒表应用。项目中使用 ArkUI 组件完成页面布局,通过@State管理状态数据,实现开始/暂停/重置计时、计次功能、计次记录列表、毫秒级精度显示和大字体时间显示等功能。

最终运行效果如下:

页面主要包含以下内容:

  • 顶部应用标题;
  • 大字体时间显示(分:秒.毫秒);
  • 开始/暂停按钮;
  • 计次/重置按钮;
  • 计次记录列表;
  • 计次序号、分段时间和总时间;
  • 空状态提示;
  • 页面整体采用 ArkUI 声明式布局。

本文重点是演示如何在 HarmonyOS 项目中使用 ArkTS 和 ArkUI 实现一个精准计时类单页面应用。项目代码主要写在entry/src/main/ets/pages/Index.ets文件中,适合作为 HarmonyOS ArkTS 入门到进阶之间的练习案例。

前言

秒表是生活中常用的计时工具,无论是运动计时、实验记录还是游戏测速,都需要一个精准易用的秒表。手机自带的秒表往往功能简单,而我们可以自己实现一个功能完整、界面美观的秒表应用。

从应用开发角度来看,这个项目不依赖后端接口,也不需要数据库,但能练习 ArkTS 中的状态管理、高精度定时器、时间格式化、列表渲染和按钮交互等内容。

本文基于 HarmonyOS 和 ArkTS 实现一个精准秒表应用。用户可以开始、暂停和重置计时,记录计次分段时间,查看每次计次的详细数据。

这个项目的核心不是简单的数字累加,而是精准的时间控制和计次功能。每一次开始、暂停、计次和重置,本质上都是对计时器状态的管理。状态变化后,页面会自动刷新,这正是 ArkUI 声明式开发的基本思想。

一、项目目标

本次实践主要实现以下目标:

  • 创建 HarmonyOS ArkTS 页面;
  • 使用@Entry@Component定义页面组件;
  • 使用@State管理页面状态;
  • 使用高精度定时器实现毫秒级计时;
  • 实现开始/暂停功能;
  • 实现重置功能;
  • 实现计次功能;
  • 格式化时间显示(分:秒.毫秒);
  • 记录每次计次的分段时间;
  • 记录每次计次的总时间;
  • 使用ListForEach渲染计次记录;
  • 使用空状态提示优化无计次页面;
  • 使用@Builder封装按钮和计次项;
  • 完成一个可以运行的秒表页面。

这个项目虽然是单页面应用,但它有完整的计时控制、计次记录和时间显示功能,比普通静态页面更适合练习 ArkTS。

二、技术栈

类型内容
开发方向HarmonyOS 应用开发
开发语言ArkTS
UI 框架ArkUI
SDK 版本HarmonyOS API 23 及以上
工程模型Stage 模型
核心组件Text / Button / Column / Row / List / ForEach
状态管理@State
数据处理时间计算 / 高精度计时 / 数组操作
项目入口entry/src/main/ets/pages/Index.ets
运行平台模拟器或真机

本项目是 HarmonyOS 原生 ArkTS 项目。页面主体由 ArkUI 组件构建,核心逻辑写在Index.ets文件中,不依赖后端接口,也不需要额外配置数据库。

三、为什么选择秒表项目

精准秒表应用适合作为 ArkTS 练习项目,主要有以下几个原因。

第一,实用性强。秒表是非常经典的工具类应用,在很多场景下都会用到。

第二,业务场景真实。运动、实验、游戏等场景都需要计时功能,是手机必备工具之一。

第三,适合练习状态管理。运行状态、经过时间、开始时间、计次记录都属于页面状态。状态变化后,页面会自动刷新。

第四,适合练习定时器使用。高精度计时需要使用定时器,这是前端开发中重要的基础技能。

第五,适合练习时间格式化。将毫秒数格式化为易读的分秒毫秒格式,是常见的开发需求。

第六,扩展空间比较大。基础功能完成后,可以继续增加倒计时、倒计时+秒表切换、计时分享、本地保存记录和多组计时功能。

在本项目中,计时器状态和计次记录是核心数据。页面中的时间显示和计次列表都由它们计算或渲染得到。

四、功能规则说明

计时精度:10毫秒(0.01秒),更新频率100次/秒。

按钮状态规则:

当前状态主按钮副按钮
初始状态开始重置(禁用)
运行中暂停计次
暂停中开始重置

计次规则:

  • 点击计次按钮记录当前时间点
  • 分段时间 = 当前总时间 - 上一次计次总时间
  • 计次按倒序显示,最新的在最上面
  • 最多记录99次计次

时间显示格式:MM:SS.cc,例如01:23.45表示1分23秒45。

五、项目结构

本项目主要修改首页文件:

entry └── src └── main └── ets └── pages └── Index.ets

其中:

文件作用
Index.ets编写页面结构、状态数据和秒表计时逻辑

本文不涉及复杂路由,也不需要额外创建多个页面。对于练习项目来说,把主要逻辑集中在一个Index.ets文件中更方便理解。

如果后续继续扩展,可以考虑把时间显示组件、控制按钮和计次项拆分成独立组件。

六、核心实现思路

本项目的核心流程如下:

  1. 定义计次记录数据结构;
  2. 使用@State保存运行状态、经过时间、定时器ID和计次记录;
  3. 点击开始按钮启动定时器;
  4. 每10毫秒更新一次经过时间;
  5. 点击暂停按钮停止定时器;
  6. 点击计次按钮记录当前时间;
  7. 计算分段时间和总时间;
  8. 点击重置按钮清空所有数据;
  9. 格式化时间显示为易读格式;
  10. 使用ForEach渲染计次记录列表;
  11. 根据当前状态动态改变按钮文字和功能;
  12. 没有计次时显示空状态提示。

项目中最重要的状态变量如下:

@State isRunning: boolean = false @State elapsedTime: number = 0 @State timerId: number = -1 @State lastLapTime: number = 0 @State laps: LapRecord[] = [] @State nextLapNumber: number = 1

其中:

状态变量作用
isRunning计时器是否运行中
elapsedTime经过的总时间(毫秒)
timerId定时器ID
lastLapTime上一次计次的时间
laps计次记录数组
nextLapNumber下一个计次的序号

elapsedTimelaps是本项目中最重要的数据。时间显示和计次列表都依赖这两个状态。

七、Index.ets 完整代码

打开文件:

entry/src/main/ets/pages/Index.ets

将其中内容替换为下面代码:

interfaceLapRecord{number:numbersplitTime:numbertotalTime:number}@Entry@Componentstruct Index{@StateisRunning:boolean=false@StateelapsedTime:number=0@StatetimerId:number=-1@StatelastLapTime:number=0@StatenextLapNumber:number=1@Statelaps:LapRecord[]=[]privatestartTime:number=0aboutToDisappear(){this.stopTimer()}privatestartTimer():void{if(this.isRunning)returnthis.isRunning=truethis.startTime=Date.now()-this.elapsedTimethis.timerId=setInterval(()=>{this.elapsedTime=Date.now()-this.startTime},10)}privatepauseTimer():void{if(!this.isRunning)returnthis.isRunning=falsethis.stopTimer()}privatestopTimer():void{if(this.timerId!==-1){clearInterval(this.timerId)this.timerId=-1}}privateresetTimer():void{this.pauseTimer()this.elapsedTime=0this.lastLapTime=0this.nextLapNumber=1this.laps=[]}privaterecordLap():void{if(!this.isRunning)returnif(this.laps.length>=99)returnletsplitTime=this.elapsedTime-this.lastLapTimeletlap:LapRecord={number:this.nextLapNumber,splitTime:splitTime,totalTime:this.elapsedTime}this.laps=[lap,...this.laps]this.lastLapTime=this.elapsedTimethis.nextLapNumber++}privateformatTime(ms:number):string{letminutes=Math.floor(ms/60000)letseconds=Math.floor((ms%60000)/1000)letcentiseconds=Math.floor((ms%1000)/10)return`${this.formatTwo(minutes)}:${this.formatTwo(seconds)}.${this.formatTwo(centiseconds)}`}privateformatTwo(value:number):string{returnvalue<10?'0'+value.toString():value.toString()}privategetFastestLap():number{if(this.laps.length<2)return-1letminTime=Math.min(...this.laps.map(l=>l.splitTime))returnthis.laps.find(l=>l.splitTime===minTime)?.number||-1}privategetSlowestLap():number{if(this.laps.length<2)return-1letmaxTime=Math.max(...this.laps.map(l=>l.splitTime))returnthis.laps.find(l=>l.splitTime===maxTime)?.number||-1}@BuilderControlButton(text:string,color:string,bgColor:string,onClick:()=>void){Button(text).width(80).height(80).fontSize(18).fontColor(color).backgroundColor(bgColor).borderRadius(40).onClick(onClick)}@BuilderLapItem(lap:LapRecord){letfastest=this.getFastestLap()letslowest=this.getSlowestLap()lettextColor='#182431'if(lap.number===fastest){textColor='#10B981'}elseif(lap.number===slowest){textColor='#EF4444'}Row(){Text(`计次${lap.number}`).fontSize(15).fontColor('#6B7280').width(80)Text(this.formatTime(lap.splitTime)).fontSize(16).fontWeight(FontWeight.Medium).fontColor(textColor).layoutWeight(1).textAlign(TextAlign.Center)Text(this.formatTime(lap.totalTime)).fontSize(16).fontColor('#182431').width(100).textAlign(TextAlign.End)}.width('100%').padding(16).margin({bottom:1}).backgroundColor(Color.White)}build(){Column(){Text('秒表').fontSize(28).fontWeight(FontWeight.Bold).fontColor('#182431').margin({top:22})Text('基于 HarmonyOS ArkTS 的精准计时工具').fontSize(14).fontColor('#6B7280').margin({top:8,bottom:40})Text(this.formatTime(this.elapsedTime)).fontSize(72).fontWeight(FontWeight.Bold).fontColor('#182431').fontFamily('monospace').textAlign(TextAlign.Center).margin({bottom:60})Row(){if(this.isRunning){this.ControlButton('计次','#0A59F7','#EFF6FF',()=>{this.recordLap()})}else{this.ControlButton('重置',this.elapsedTime>0?'#EF4444':'#9CA3AF',this.elapsedTime>0?'#FEF2F2':'#F3F4F6',()=>{this.resetTimer()})}Blank()if(this.isRunning){this.ControlButton('暂停','#F59E0B','#FEF3C7',()=>{this.pauseTimer()})}else{this.ControlButton('开始','#10B981','#D1FAE5',()=>{this.startTimer()})}}.width('100%').padding({left:30,right:30}).margin({bottom:30})if(this.laps.length>0){Row(){Text('计次').fontSize(13).fontColor('#9CA3AF').width(80)Text('分段时间').fontSize(13).fontColor('#9CA3AF').layoutWeight(1).textAlign(TextAlign.Center)Text('总时间').fontSize(13).fontColor('#9CA3AF').width(100).textAlign(TextAlign.End)}.width('100%').padding({left:16,right:16,top:12,bottom:12}).backgroundColor('#F9FAFB')}if(this.laps.length===0){Column(){Text('暂无计次记录').fontSize(16).fontColor('#6B7280')Text('开始计时后点击"计次"按钮记录分段时间').fontSize(13).fontColor('#9CA3AF').margin({top:8})}.width('100%').padding(30).backgroundColor(Color.White).borderRadius(16)}else{List(){ForEach(this.laps,(lap:LapRecord)=>{ListItem(){this.LapItem(lap)}},(lap:LapRecord)=>lap.number.toString())}.width('100%').layoutWeight(1).scrollBar(BarState.Off).borderRadius(16).clip(true)}}.width('100%').height('100%').padding({left:18,right:18}).backgroundColor('#F5F7FA')}}

八、代码实现说明

1. 高精度计时

使用Date.now()获取时间戳,计算经过的时间,而不是简单累加,这样可以避免定时器误差累积:

this.startTime=Date.now()-this.elapsedTimethis.timerId=setInterval(()=>{this.elapsedTime=Date.now()-this.startTime},10)

每10毫秒更新一次,实现10毫秒精度。

2. 时间格式化

将毫秒数格式化为MM:SS.cc格式:

privateformatTime(ms:number):string{letminutes=Math.floor(ms/60000)letseconds=Math.floor((ms%60000)/1000)letcentiseconds=Math.floor((ms%1000)/10)return`${this.formatTwo(minutes)}:${this.formatTwo(seconds)}.${this.formatTwo(centiseconds)}`}
3. 按钮状态管理

根据当前运行状态动态改变按钮的文字、颜色和功能:

  • 未运行且时间为0:开始/重置(禁用)
  • 运行中:暂停/计次
  • 暂停中:开始/重置
4. 计次功能

记录每次计次的分段时间和总时间,最快计次显示绿色,最慢计次显示红色:

letsplitTime=this.elapsedTime-this.lastLapTimeletlap:LapRecord={number:this.nextLapNumber,splitTime:splitTime,totalTime:this.elapsedTime}
5. 定时器清理

页面销毁时清除定时器,避免内存泄漏:

aboutToDisappear(){this.stopTimer()}

九、运行项目

代码编写完成后,在DevEco Studio中运行项目。测试开始/暂停/重置、计次功能,检查时间显示是否准确,计次记录是否正确。

十、开发中遇到的问题

  • 定时器误差问题:使用时间戳计算而不是累加,避免误差累积
  • 按钮状态切换:根据不同状态显示不同按钮
  • 最快最慢计次标记:自动识别并高亮显示
  • 等宽字体:使用monospace字体让数字对齐更美观

十一、总结

本文基于HarmonyOS和ArkTS实现了一个精准秒表应用。项目通过@State管理计时状态,使用高精度定时器实现毫秒级计时,实现了开始/暂停/重置、计次记录和时间格式化等功能。这个项目展示了ArkTS中定时器使用、时间计算和状态管理的基本方法,适合作为入门练习项目。

http://www.jsqmd.com/news/1269790/

相关文章:

  • 2026年长三角屋顶隔热施工公司评测:稀土隔热涂层实测降温20℃+
  • 微服务化的基石——持续集成
  • Windows 10 OneDrive 终极卸载指南:彻底移除预装云盘工具
  • 如何快速掌握Nginx配置安全:终极完整指南
  • Agent Skills开发实战:从架构设计到生产部署
  • HarmonyOS应用《玄象》开发实战:SplashPage 全屏暗色背景与 Stack 层叠布局
  • 河源紫金黄金奢侈品回收哪家靠谱?5家优质正规门店精选推荐 - 紫金的金
  • 终极指南:如何用REFramework彻底改变你的RE引擎游戏体验
  • 如何快速上手core.matrix:从安装到创建第一个矩阵的完整教程
  • 深度学习模型训练震荡问题分析与解决方案
  • 如何在3分钟内实现Windows远程文件管理:SSHFS图形界面完整教程
  • MCAN/CAN FD通信故障排查实战指南:从硬件到软件的深度调试
  • 全新升级三洋洗衣机售后服务电话24小时人工专属热线正式启用公告 - AAA家电服务指南
  • 大模型预训练全流程技术解析与实战优化
  • 5分钟掌握BilibiliDown:一站式B站视频下载与批量处理终极指南
  • 2026潮汕旅游避坑指南:怎么选靠谱导游?本地人带你玩转潮州汕头 - 纯玩旅游推荐官
  • C++静态分析工具深度对比:PC-lint Plus、Polyspace与SonarQube选型指南
  • NetworkX扩展库Little Ball of Fur:如何用统一API实现20+采样算法?
  • 腾讯开源AI作曲神器:三步上手专业级歌曲生成完整指南
  • Demo 跑通只是热身:权限与日志才是 AI 工程化的生死线
  • TI CC27xx SPI高级功能解析:自动CRC与原子化头部更新实战
  • 深度强化学习在智能电网电压控制中的应用与实践
  • HarmonyOS开发实战:笔友-@Reusable 组件复用在长列表中的内存收益
  • 当AI学会谱曲:腾讯SongGeneration如何让每个人成为音乐创作者
  • 基于YOLOv5的道路裂缝检测系统设计与优化
  • 昆明汽车改装认准鹏德车坊|专业轮毂改装,颜值性能一步到位 - 英特菲斯
  • 终极指南:如何用ESPHome-Flasher轻松搞定ESP设备刷机?
  • 抖音无水印下载神器:douyin-downloader 5分钟快速入门指南
  • 漏洞解析--XSS 从入门到精通!
  • YOLOv11在工业质检中的高效缺陷检测实践