番茄钟工具功能聚焦、交互逻辑清晰,是鸿蒙应用开发入门阶段理解组件化开发与状态管理的优质实践项目。这篇文章将以一款可直接运行的 ArkTS 番茄钟应用为核心,从项目搭建、代码解析到功能扩展,一步步拆解开发细节,确保每部分内容都贴合实际开发场景,既能帮助大家夯实基础开发知识,也能提供可直接复用的完整实现方案。
一、项目核心介绍
1. 应用定位
这是一款轻量化时间管理工具,核心功能包括:
- 6 档专注时长可选(5/10/15/30/45/60 分钟)
- 实时计时反馈与动态视觉提示
- 完整状态控制(开始 / 暂停 / 重置 / 归零)
- 计时完成提醒功能
2. 技术栈
- 开发语言:ArkTS(鸿蒙首选开发语言,语法与 TypeScript 相近,上手难度较低)
- 开发工具:DevEco Studio(鸿蒙官方 IDE,免费下载配置即可使用)
- 核心技术:组件化开发、状态管理(@State 装饰器)、定时器控制、响应式布局
3. 代码架构
采用 "状态 - 逻辑 - 视图" 三层结构,让代码层次更清晰,便于维护和扩展:
- 状态层:通过 @State 装饰器管理计时状态、剩余时间、界面显示控制等核心变量
- 逻辑层:将计时控制、动画管理、时间格式化等重复逻辑封装为独立方法,减少冗余
- 视图层:利用 ArkTS 内置组件(Column/Stack/Text/Button 等)搭建响应式界面,实现状态与视图的自动联动
二、开发前准备
- 安装 DevEco Studio:前往鸿蒙官网下载最新版本,按照安装向导完成配置,记得勾选鸿蒙 SDK 的相关组件
- 创建项目:选择 "Empty Ability" 模板,设备类型选择 "Phone",开发语言设置为 "ArkTS"
- 替换代码:项目创建完成后,找到
entry/src/main/ets/pages/Index.ets文件,将本文末尾的完整代码替换进去,即可直接运行调试
三、核心代码逐句解析
1. 状态变量:应用运行的 "数据基石"
首先定义核心状态变量,用 @State 装饰器标记后,变量发生变化时界面会自动更新,这是鸿蒙响应式开发的核心特性:
@State currentTime: string = this.getFormattedTime() // 存储并显示当前系统时间(格式:时:分:秒) @State focusMinutes: number = 5 // 当前选择的专注时长,默认5分钟 @State remainingSeconds: number = 5 * 60 // 倒计时剩余秒数,随计时动态变化 @State isRunning: boolean = false // 标记计时是否运行,控制开始/暂停按钮文本切换 @State timerId: number | null = null // 计时定时器ID,用于暂停时释放资源 @State showCompletion: boolean = false // 标记计时是否结束,控制完成提示的显示/隐藏 @State progressValue: number = 0 // 进度值,预留用于后续进度条扩展 @State showTimeOptions: boolean = true // 控制时间设置按钮组的显示,计时时隐藏避免误操作 @State isZeroState: boolean = false // 标记是否处于归零状态,区分正常计时与归零操作 @State showAllButtons: boolean = false // 控制归零、重置按钮的显示,仅计时/暂停时可见 @State dotIndex: number = 0 // 控制动态圆点闪烁索引,实现轮流亮灭效果 @State dotTimerId: number | null = null // 圆点动画定时器ID,用于同步停止动画同时定义两个固定颜色常量,保证界面视觉风格统一:
private readonly ACTIVE_DOT_COLOR: Color = 0x4DFF9E // 圆点点亮状态(亮绿色) private readonly INACTIVE_DOT_COLOR: Color = 0x4DFF9E30 // 圆点熄灭状态(半透明绿色)2. 工具方法:功能逻辑的 "封装单元"
将重复使用的逻辑封装为独立方法,既能提升代码可读性,也方便后续重复调用,这些方法是连接状态变量与用户操作的关键:
(1)时间格式化方法
解决时间显示不规范的问题,确保输出格式统一为两位数:
// 获取格式化的系统时间,用于顶部时间显示 getFormattedTime(): string { const now = new Date() // 获取当前系统时间 // padStart(2, '0') 确保时、分、秒均为两位数,不足补0 return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}` } // 将秒数格式化为"分:秒"形式,用于核心计时区显示 formatTime(seconds: number): string { const mins = Math.floor(seconds / 60) // 计算分钟数 const secs = seconds % 60 // 计算剩余秒数 return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` }(2)动画控制方法
通过控制三个圆点的轮流闪烁,提供直观的计时视觉反馈:
// 开启动点闪烁动画 startDotAnimation() { if (this.dotTimerId !== null) { clearInterval(this.dotTimerId) // 避免重复创建定时器导致动画异常 } // 每300毫秒更新一次圆点索引,实现轮流点亮效果 this.dotTimerId = setInterval(() => { this.dotIndex = (this.dotIndex + 1) % 3 // 取余3确保索引在0-2之间循环 }, 300) } // 停止圆点闪烁动画 stopDotAnimation() { if (this.dotTimerId !== null) { clearInterval(this.dotTimerId) // 清除动画定时器 this.dotTimerId = null } this.dotIndex = 0 // 重置索引,确保下次启动从第一个圆点开始 } // 根据计时状态和索引,返回对应的圆点颜色 getDotColor(index: number): Color { if (!this.isRunning) { return Color.Transparent // 非计时状态下,圆点显示透明 } // 计时状态下,当前索引的圆点点亮,其他为熄灭状态 return index === this.dotIndex ? this.ACTIVE_DOT_COLOR : this.INACTIVE_DOT_COLOR }(3)生命周期与时间更新方法
aboutToAppear () 是鸿蒙组件的生命周期钩子,在组件即将显示时自动执行:
// 组件即将显示时启动系统时间更新定时器 aboutToAppear() { // 每1秒调用一次updateTime方法,确保顶部时间实时同步系统时间 setInterval(() => { this.updateTime() }, 1000) } // 更新当前系统时间 updateTime() { this.currentTime = this.getFormattedTime() }3. 核心功能逻辑:应用的 "核心交互"
这部分实现了计时控制、重置、归零、时长选择等核心功能,是整个应用的逻辑核心:
(1)开始 / 暂停计时(toggleTimer ())
根据 isRunning 状态判断当前操作是开始还是暂停,同步管理定时器与界面状态:
toggleTimer() { if (this.isRunning) { // 情况1:当前正在计时,执行暂停操作 if (this.timerId !== null) { clearInterval(this.timerId) // 停止计时定时器,释放资源 this.timerId = null } this.stopDotAnimation() // 同步停止圆点动画 this.isRunning = false // 标记为暂停状态 this.showAllButtons = true // 显示归零和重置按钮 } else { // 情况2:当前未计时,执行开始操作 // 若之前计时已结束,重置结束状态 if (this.showCompletion) { this.showCompletion = false this.remainingSeconds = this.focusMinutes * 60 this.progressValue = 0 } // 若处于归零状态,恢复计时初始值 if (this.isZeroState) { this.remainingSeconds = this.focusMinutes * 60 this.progressValue = 0 this.isZeroState = false } this.showTimeOptions = false // 隐藏时长选择按钮,避免计时中误改 this.isRunning = true // 标记为运行状态 this.showAllButtons = true // 显示归零和重置按钮 this.startDotAnimation() // 开启动点闪烁动画 // 创建计时定时器,每1秒更新一次倒计时 this.timerId = setInterval(() => { if (this.remainingSeconds > 0) { this.remainingSeconds-- // 剩余秒数减1 // 更新进度值,进度从0到1递增 this.progressValue = 1 - (this.remainingSeconds / (this.focusMinutes * 60)) } else { // 计时结束,清理资源并更新状态 clearInterval(this.timerId) this.timerId = null this.isRunning = false this.showCompletion = true // 显示计时完成提示 this.showAllButtons = true this.stopDotAnimation() // 停止动画 } }, 1000) } }(2)重置计时(resetTimer ())
点击 "重置" 按钮时,将应用恢复至初始状态:
resetTimer() { if (this.timerId !== null) { clearInterval(this.timerId) // 停止计时定时器 this.timerId = null } this.stopDotAnimation() // 停止圆点动画 this.isRunning = false // 标记为未运行状态 this.showCompletion = false // 隐藏完成提示 this.remainingSeconds = this.focusMinutes * 60 // 恢复剩余时间为选中时长 this.progressValue = 0 // 进度值归零 this.showTimeOptions = true // 显示时长选择按钮 this.isZeroState = false // 取消归零标记 this.showAllButtons = false // 隐藏归零和重置按钮 }(3)归零功能(zeroTimer ())
与 "重置" 不同,"归零" 直接将倒计时设为 00:00,方便快速结束当前计时:
zeroTimer() { if (this.timerId !== null) { clearInterval(this.timerId) // 停止计时定时器 this.timerId = null } this.stopDotAnimation() // 停止圆点动画 this.isRunning = false // 标记为未运行状态 this.showCompletion = false // 隐藏完成提示 this.remainingSeconds = 0 // 剩余秒数设为0 this.progressValue = 1 // 进度值设为1(满进度) this.showTimeOptions = true // 显示时长选择按钮 this.isZeroState = true // 标记为归零状态 this.showAllButtons = false // 隐藏归零和重置按钮 }(4)选择专注时长(setFocusTime ())
点击时长按钮时触发,根据当前状态更新专注时长和倒计时:
setFocusTime(minutes: number) { this.focusMinutes = minutes // 更新选中的专注时长 // 仅在归零状态或未计时时,同步更新剩余秒数,避免计时中打断 if (this.isZeroState || !this.isRunning) { this.remainingSeconds = minutes * 60 this.progressValue = 0 } }4. 界面实现:build 方法中的响应式布局
利用 ArkTS 内置组件搭建界面,所有组件属性均与状态变量动态绑定,实现 "状态变、视图变" 的效果。界面主要分为 5 个核心区域:
(1)顶部当前时间显示
Text(this.currentTime) .fontSize(28) // 字体大小 .fontWeight(FontWeight.Bold) // 字体加粗 .margin({ top: 30, bottom: 40 }) // 上下边距设置 .fontColor("#00FFFF") // 字体颜色(青色) .textShadow({ radius: 10, color: "#FF00FF", offsetX: 0, offsetY: 0 }) // 品红色文字阴影(2)核心计时区(界面视觉中心)
采用 Stack 组件实现背景与内容的叠加布局,提升界面层次感:
Stack() { // 背景层:深灰色圆角矩形,带青色虚线边框 Column() {} .width('90%') .height(320) .backgroundColor("#0F1923") .borderRadius(16) // 圆角设置为16px .border({ width: 2, color: "#00FFFF", style: BorderStyle.Dashed // 虚线边框 }) .padding(25) .opacity(0.9) // 透明度设置为0.9 // 内容层:倒计时文本 + 闪烁圆点 Column() { // 倒计时文本:大号亮绿色,带蓝色文字阴影 Text(this.formatTime(this.remainingSeconds)) .fontSize(48) .fontWeight(FontWeight.Bold) .fontColor("#00FFAA") .textShadow({ radius: 8, color: "#0000FF", offsetX: 0, offsetY: 0 }) // 闪烁圆点区域:3个圆点循环亮灭 Row() { // 通过ForEach循环创建3个圆点(数组[0,1,2]对应3个圆点) ForEach([0, 1, 2], (i: number) => { Circle() // 圆形组件 .width(14) .height(14) .fill(this.getDotColor(i)) // 绑定圆点颜色方法 .margin(6) // 圆点之间的间距 .opacity(this.isRunning ? 1 : 0.3) // 计时时不透明,否则半透明 }) } .margin({ top: 10 }) .height(30) } } .margin({ bottom: 40 })(3)计时完成提示区
通过 showCompletion 状态控制显示,仅在计时结束时展示:
if (this.showCompletion) { Column() { Text('TIME UP!') // 计时结束提示文本 .fontSize(32) .fontWeight(FontWeight.Bold) .fontColor("#FF5555") // 红色字体 .textShadow({ radius: 5, color: Color.White, offsetX: 0, offsetY: 0 }) Text('Take a break now') // 休息提示文本 .fontSize(18) .fontColor("#AAAAFF") // 浅蓝色字体 .margin({ top: 10 }) } .width('100%') .padding(20) .backgroundColor("#000000") // 黑色背景 .border({ width: 3, color: "#FF00FF" }) // 品红色边框 .margin({ bottom: 30 }) .opacity(0.95) }(4)时长选择按钮区
通过 showTimeOptions 状态控制显示,仅在未计时或重置后展示,用 Scroll 组件适配小屏幕:
if (this.showTimeOptions) { Scroll() { Row() { // 循环创建6个时长按钮(5/10/15/30/45/60分钟) ForEach([5, 10, 15, 30, 45, 60], (minutes: number) => { Column() { Text(`${minutes}`) // 时长数字(大号加粗) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(this.focusMinutes === minutes ? "#00FFCC" : "#888888") // 选中时亮青色 Text('分钟') // 时长单位(小号) .fontSize(12) .fontColor(this.focusMinutes === minutes ? "#FFFFFF" : "#666666") } .width(70) .height(70) .borderRadius(35) // 圆形按钮(宽高70px,圆角35px) .backgroundColor(this.focusMinutes === minutes ? "#333333" : "#111111") // 选中时深灰色背景 .justifyContent(FlexAlign.Center) // 文字居中显示 .onClick(() => this.setFocusTime(minutes)) // 绑定时长选择方法 .margin(8) // 按钮之间的间距 }) } .padding(10) } .scrollable(ScrollDirection.Horizontal) // 水平滚动 .margin({ bottom: 30 }) }(5)操作按钮区(开始 / 暂停 / 归零 / 重置)
Row() { // 左侧:归零按钮(仅showAllButtons为true时显示) if (this.showAllButtons) { Button('归零', { type: ButtonType.Normal }) .width(100) .height(50) .backgroundColor("#770077") // 紫色背景 .fontColor(Color.White) .fontSize(18) .shadow({ radius: 5, color: "#FF00FF", offsetX: 0, offsetY: 3 }) // 品红色阴影 .onClick(() => this.zeroTimer()) // 绑定归零方法 } else { Blank().width(100).height(50) // 不显示时用空白占位,保持布局整齐 } // 中间:开始/暂停按钮(始终显示) Button(this.isRunning ? '暂停' : '开始', { type: ButtonType.Normal }) .width(100) .height(50) .backgroundColor(this.isRunning ? "#FF3366" : "#33CC99") // 暂停时粉红,开始时绿色 .fontColor(Color.White) .fontSize(20) .fontWeight(FontWeight.Bold) .shadow({ radius: 8, color: this.isRunning ? "#FF0000" : "#00FF00", offsetX: 0, offsetY: 5 }) // 阴影随状态切换 .onClick(() => this.toggleTimer()) // 绑定开始/暂停方法 .margin({ left: 10, right: 10 }) // 左右间距 // 右侧:重置按钮(仅showAllButtons为true时显示) if (this.showAllButtons) { Button('重置', { type: ButtonType.Normal }) .width(100) .height(50) .backgroundColor("#555577") // 深蓝色背景 .fontColor(Color.White) .fontSize(18) .shadow({ radius: 5, color: "#0000FF", offsetX: 0, offsetY: 3 }) // 蓝色阴影 .onClick(() => this.resetTimer()) // 绑定重置方法 } else { Blank().width(100).height(50) // 空白占位 } } .width('100%') .justifyContent(FlexAlign.Center) // 按钮组居中显示 .margin({ top: 20 }) }四、常见问题与解决方法
开发过程中遇到了几个典型问题,总结了对应的解决思路,希望能帮助大家少踩坑:
1. 定时器重复创建(计时加速)
- 问题:多次点击 "开始" 按钮,会创建多个定时器,导致倒计时速度异常加快
- 解决:创建定时器前先判断 timerId 是否为 null,确保每次仅存在一个运行中的定时器;暂停时及时清除 timerId 并设为 null
2. 动画与计时不同步
- 问题:暂停计时后,圆点动画仍然继续闪烁,状态不一致
- 解决:为动画单独设置 dotTimerId,在暂停、重置、归零时都调用 stopDotAnimation () 方法,确保动画与计时状态同步
3. 小屏幕按钮遮挡
- 问题:在屏幕尺寸较小的设备上,时长选择按钮会显示不全
- 解决:用 Scroll 组件包裹时长按钮组,设置为水平滚动(ScrollDirection.Horizontal),适配不同屏幕尺寸
五、功能扩展思路
掌握基础功能后,可以尝试扩展以下功能,进一步提升应用实用性,同时加深对鸿蒙开发的理解:
1. 添加进度条显示
利用已有的 progressValue 变量(取值范围 0-1),添加 Progress 组件展示计时进度:
Progress() .value(this.progressValue) // 绑定进度值 .type(ProgressType.Linear) // 线性进度条 .width('80%') .height(10) .color("#00FFAA") // 进度条颜色 .backgroundColor("#333333") // 进度条背景色 .margin({ top: 20 })2. 增加休息计时功能
遵循番茄工作法的核心逻辑,添加休息计时模块:
- 新增休息时长选择(如 5/10 分钟)
- 实现工作 / 休息状态切换逻辑
- 休息时间结束时添加对应的提示
3. 自定义提示音
计时结束时播放提示音,提升用户体验:
// 导入媒体组件 import media from '@ohos.multimedia.media'; // 定义播放提示音方法 async playAlertSound() { const player = media.createAudioPlayer(); await player.src('common/sound/alert.mp3'); // 将提示音文件放入对应目录 await player.play(); } // 在计时结束时调用(toggleTimer()方法的else分支中) this.playAlertSound();4. 专注统计功能
记录并展示专注时长数据:
- 使用鸿蒙数据存储 API(如 Preferences)保存每日 / 每周专注记录
- 新增统计页面,用图表展示专注时长分布
- 支持按日期查询历史专注记录
5. 自定义主题颜色
让用户可以根据喜好选择界面主题:
- 新增主题选择页面,提供多种主题方案(如浅色 / 深色 / 彩色)
- 用 @Link 装饰器实现主题颜色变量的跨组件传递
- 将代码中固定的颜色值替换为主题变量
六、完整可运行代码
@Entry @Component struct TomatoTimer { @State currentTime: string = this.getFormattedTime() @State focusMinutes: number = 5 // 默认改为5分钟 @State remainingSeconds: number = 5 * 60 // 默认改为5分钟 @State isRunning: boolean = false @State timerId: number | null = null @State showCompletion: boolean = false @State progressValue: number = 0 @State showTimeOptions: boolean = true @State isZeroState: boolean = false @State showAllButtons: boolean = false @State dotIndex: number = 0 // 当前闪烁的圆点索引 @State dotTimerId: number | null = null // 圆点动画计时器ID // 圆点颜色定义 private readonly ACTIVE_DOT_COLOR: Color = 0x4DFF9E // 亮绿色 private readonly INACTIVE_DOT_COLOR: Color = 0x4DFF9E30 // 半透明绿色 // 获取格式化时间 getFormattedTime(): string { const now = new Date() return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}` } // 更新当前时间 updateTime() { this.currentTime = this.getFormattedTime() } // 开始圆点动画 startDotAnimation() { if (this.dotTimerId !== null) { clearInterval(this.dotTimerId) } this.dotTimerId = setInterval(() => { this.dotIndex = (this.dotIndex + 1) % 3 }, 300) // 每300毫秒切换一次圆点 } // 停止圆点动画 stopDotAnimation() { if (this.dotTimerId !== null) { clearInterval(this.dotTimerId) this.dotTimerId = null } this.dotIndex = 0 } // 获取圆点颜色 getDotColor(index: number): Color { if (!this.isRunning) { return Color.Transparent // 非计时时间不显示颜色 } return index === this.dotIndex ? this.ACTIVE_DOT_COLOR : this.INACTIVE_DOT_COLOR } // 开始/暂停计时器 toggleTimer() { if (this.isRunning) { // 暂停计时器 if (this.timerId !== null) { clearInterval(this.timerId) this.timerId = null } this.stopDotAnimation() this.isRunning = false this.showAllButtons = true } else { // 开始计时器 if (this.showCompletion) { this.showCompletion = false this.remainingSeconds = this.focusMinutes * 60 this.progressValue = 0 } if (this.isZeroState) { this.remainingSeconds = this.focusMinutes * 60 this.progressValue = 0 this.isZeroState = false } this.showTimeOptions = false this.isRunning = true this.showAllButtons = true this.startDotAnimation() this.timerId = setInterval(() => { if (this.remainingSeconds > 0) { this.remainingSeconds-- this.progressValue = 1 - (this.remainingSeconds / (this.focusMinutes * 60)) } else { clearInterval(this.timerId) this.timerId = null this.isRunning = false this.showCompletion = true this.showAllButtons = true this.stopDotAnimation() } }, 1000) } } // 重置计时器 resetTimer() { if (this.timerId !== null) { clearInterval(this.timerId) this.timerId = null } this.stopDotAnimation() this.isRunning = false this.showCompletion = false this.remainingSeconds = this.focusMinutes * 60 this.progressValue = 0 this.showTimeOptions = true this.isZeroState = false this.showAllButtons = false } // 归零功能 zeroTimer() { if (this.timerId !== null) { clearInterval(this.timerId) this.timerId = null } this.stopDotAnimation() this.isRunning = false this.showCompletion = false this.remainingSeconds = 0 this.progressValue = 1 this.showTimeOptions = true this.isZeroState = true this.showAllButtons = false } // 设置专注时间 setFocusTime(minutes: number) { this.focusMinutes = minutes if (this.isZeroState || !this.isRunning) { this.remainingSeconds = minutes * 60 this.progressValue = 0 } } // 格式化倒计时显示 formatTime(seconds: number): string { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` } aboutToAppear() { setInterval(() => { this.updateTime() }, 1000) } build() { Column() { // 顶部当前时间显示 Text(this.currentTime) .fontSize(28) .fontWeight(FontWeight.Bold) .margin({ top: 30, bottom: 40 }) .fontColor("#00FFFF") .textShadow({ radius: 10, color: "#FF00FF", offsetX: 0, offsetY: 0 }) // 番茄钟主体 Stack() { Column() {} .width('90%') .height(320) .backgroundColor("#0F1923") .borderRadius(16) .border({ width: 2, color: "#00FFFF", style: BorderStyle.Dashed }) .padding(25) .opacity(0.9) Column() { Text(this.formatTime(this.remainingSeconds)) .fontSize(48) .fontWeight(FontWeight.Bold) .fontColor("#00FFAA") .textShadow({ radius: 8, color: "#0000FF", offsetX: 0, offsetY: 0 }) // 修改后的圆点显示部分 Row() { ForEach([0, 1, 2], (i: number) => { Circle() .width(14) .height(14) .fill(this.getDotColor(i)) .margin(6) .opacity(this.isRunning ? 1 : 0.3) }) } .margin({ top: 10 }) .height(30) } } .margin({ bottom: 40 }) // 完成提示 if (this.showCompletion) { Column() { Text('TIME UP!') .fontSize(32) .fontWeight(FontWeight.Bold) .fontColor("#FF5555") .textShadow({ radius: 5, color: Color.White, offsetX: 0, offsetY: 0 }) Text('Take a break now') .fontSize(18) .fontColor("#AAAAFF") .margin({ top: 10 }) } .width('100%') .padding(20) .backgroundColor("#000000") .border({ width: 3, color: "#FF00FF" }) .margin({ bottom: 30 }) .opacity(0.95) } // 时间设置按钮 if (this.showTimeOptions) { Scroll() { Row() { ForEach([5, 10, 15, 30, 45, 60], (minutes: number) => { Column() { Text(`${minutes}`) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(this.focusMinutes === minutes ? "#00FFCC" : "#888888") Text('分钟') .fontSize(12) .fontColor(this.focusMinutes === minutes ? "#FFFFFF" : "#666666") } .width(70) .height(70) .borderRadius(35) .backgroundColor(this.focusMinutes === minutes ? "#333333" : "#111111") .justifyContent(FlexAlign.Center) .onClick(() => this.setFocusTime(minutes)) .margin(8) }) } .padding(10) } .scrollable(ScrollDirection.Horizontal) .margin({ bottom: 30 }) } // 操作按钮区域 Row() { // 左侧按钮 - 归零 if (this.showAllButtons) { Button('归零', { type: ButtonType.Normal }) .width(100) .height(50) .backgroundColor("#770077") .fontColor(Color.White) .fontSize(18) .shadow({ radius: 5, color: "#FF00FF", offsetX: 0, offsetY: 3 }) .onClick(() => this.zeroTimer()) } else { Blank() .width(100) .height(50) } // 中间按钮 - 开始/暂停 Button(this.isRunning ? '暂停' : '开始', { type: ButtonType.Normal }) .width(100) .height(50) .backgroundColor(this.isRunning ? "#FF3366" : "#33CC99") .fontColor(Color.White) .fontSize(20) .fontWeight(FontWeight.Bold) .shadow({ radius: 8, color: this.isRunning ? "#FF0000" : "#00FF00", offsetX: 0, offsetY: 5 }) .onClick(() => this.toggleTimer()) .margin({ left: 10, right: 10 }) // 右侧按钮 - 重置 if (this.showAllButtons) { Button('重置', { type: ButtonType.Normal }) .width(100) .height(50) .backgroundColor("#555577") .fontColor(Color.White) .fontSize(18) .shadow({ radius: 5, color: "#0000FF", offsetX: 0, offsetY: 3 }) .onClick(() => this.resetTimer()) } else { Blank() .width(100) .height(50) } } .width('100%') .justifyContent(FlexAlign.Center) .margin({ top: 20 }) } .width('100%') .height('100%') .padding(20) .backgroundColor("#0A0E17") .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) } }总结
这款番茄钟应用虽然功能不算复杂,但涵盖了鸿蒙开发的核心知识点,包括状态管理、组件通信、定时器控制、响应式布局等。对于刚接触鸿蒙开发的同学来说,是一个非常合适的入门实践项目。
按照本文的步骤,不仅能快速实现一个可用的番茄钟应用,还能掌握鸿蒙开发的基本思路和方法。后续可以尝试扩展更多功能,在实践中逐步提升自己的技术水平。如果在开发过程中遇到问题,建议多查阅鸿蒙官方文档,也可以和同学朋友交流探讨,共同进步。
本文在撰写过程中,参考了 CSDN 博主Rene_ZHK的优质文章《鸿蒙ArkTS打造高效番茄钟》,其清晰的思路和实用的分享为本文提供了重要启发。在此向原作者表示诚挚的感谢
原文链接:鸿蒙ArkTS打造高效番茄钟_鸿蒙番茄钟案例-CSDN博客