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

HarmonyOS《柚兔学伴》项目实战11-倒计时组件封装与控制器模式

第11篇:倒计时组件封装与控制器模式

在"柚兔学伴"中,番茄钟倒计时是核心功能之一。倒计时组件需要满足:外部可设置时间、可远程重置、可监听状态变化。这就引出了一个经典的组件设计问题——如何让父组件从外部控制子组件的行为?

本篇从TimerComponent.ets的 202 行源码出发,详解控制器模式(Controller Pattern)在 ArkUI 中的实现。


一、控制器模式的核心思想

ArkUI 的@Component是一个封闭的作用域,@State@Prop等状态装饰器只能在组件内部修改。如果父组件想要主动触发子组件的某个行为(如重置倒计时),单纯通过属性传递是做不到的。

控制器模式的解法是:子组件在初始化时,将自身的方法注册到控制器对象上;父组件持有控制器,通过调用控制器方法间接驱动子组件

父组件 ──调用──> Controller.reset() ──回调──> 子组件.onResetFromController()

二、TimerComponentController 实现

exportclassTimerComponentController{privateresetCallback:(()=>void)|null=null;setResetCallback(callback:()=>void){this.resetCallback=callback;}reset(){if(this.resetCallback){this.resetCallback();}}}

控制器非常简洁,只做两件事:

  1. setResetCallback:注册回调函数(由子组件调用)
  2. reset:执行回调函数(由父组件调用)

这种"注册-执行"的模式,本质是将子组件的方法引用"提升"到了控制器中,打破了组件边界对方法调用的限制。


三、TimerComponent 组件详解

3.1 属性定义

@Componentexportstruct TimerComponent{controller:TimerComponentController|null=null;@Prop@Watch('onTotalTimeChange')totalTime:number=60;onTotalTimeChange(){this.resetTimer();this.currentTime=this.totalTime;}onTimeUp?:()=>void;onTimeChange?:(currentTime:number)=>void;@StatecurrentTime:number=60;@StateisRunning:boolean=false;privatetimer:number=-1;privategradientColor:LinearGradient=newLinearGradient([{color:$r('app.color.color_color_1'),offset:0.5},{color:$r('app.color.color_color_2'),offset:1.0}])}

关键设计点:

属性装饰器说明
totalTime@Prop @Watch从父组件单向传入,变化时自动重置计时器
currentTime@State当前剩余时间,驱动 UI 刷新
isRunning@State运行状态,控制按钮文字和颜色
onTimeUp普通属性回调函数,时间归零时通知父组件
onTimeChange普通属性回调函数,每秒通知父组件时间变化
controller普通属性控制器实例,父组件通过它远程控制

@Prop保证totalTime从父组件单向流入;@Watch则监听其变化,当外部重新设置时间时自动重置。

3.2 控制器绑定

aboutToAppear(){this.currentTime=this.totalTime;if(this.controller){this.controller.setResetCallback(this.onResetFromController.bind(this));}}onResetFromController(){this.resetTimer();this.currentTime=this.totalTime;}

aboutToAppear是组件的生命周期回调,在组件首次创建时执行。此时将onResetFromController方法通过bind(this)绑定正确的this指向后,注册到控制器。

为什么需要.bind(this)因为setResetCallback存储的是函数引用,当控制器调用resetCallback()时,this可能丢失。bind确保无论谁调用这个函数,this都指向组件实例。

3.3 倒计时核心逻辑

toggleTimer(){if(this.isRunning){clearInterval(this.timer);this.isRunning=false;}else{this.isRunning=true;this.timer=setInterval(()=>{if(this.currentTime>0){this.currentTime--;if(this.onTimeChange){this.onTimeChange(this.currentTime);}}else{this.isRunning=false;clearInterval(this.timer);if(this.onTimeUp){this.onTimeUp();}}},1000);}}

每 1000ms(1秒)执行一次:

  • 剩余时间 > 0:递减currentTime,触发onTimeChange回调
  • 剩余时间 = 0:停止定时器,标记为未运行,触发onTimeUp回调

toggleTimer是一个切换函数——运行时暂停,暂停时开始。clearInterval确保不会产生多个定时器叠加。

3.4 重置逻辑

resetTimer(){clearInterval(this.timer);this.isRunning=false;this.currentTime=this.totalTime;}

重置操作包含三个步骤:清除定时器 → 停止运行状态 → 恢复初始时间。无论是本地重置还是通过控制器远程重置,最终都调用此方法。


四、时间格式化

formatTime(seconds:number):string{consthours=Math.floor(seconds/3600);constminutes=Math.floor((seconds%3600)/60);constsecs=seconds%60;return`${hours.toString().padStart(2,'0')}:${minutes.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;}

将总秒数转换为HH:MM:SS格式。padStart(2, '0')确保个位数补零,如5显示为05


五、UI 构建

5.1 圆形进度条

Stack(){Progress({value:this.getProgress(),total:this.totalTime,type:ProgressType.ScaleRing}).width('100%').height('100%').style({strokeWidth:10,}).color($r('app.color.app_primary'))Column(){Text(this.formatTime(this.currentTime)).fontSize(20).fontWeight(FontWeight.Bolder).textAlign(TextAlign.Center)Text('剩余时间').fontSize(14).fontColor(Color.Gray).margin({top:10})}}

ProgressType.ScaleRing是圆环进度条,value为已用时间(totalTime - currentTime),total为总时间。Stack 布局将时间文字叠加在进度环中心。

5.2 控制按钮

Row({space:12}){Button('重置').fontSize(12).height(30).backgroundColor('#F0F0F0').fontColor(Color.Black).borderRadius(25).onClick(()=>{this.resetTimer();})Button(this.isRunning?'暂停':'开始').fontSize(12).height(30).backgroundColor(this.isRunning?$r('app.color.app_alert'):$r('app.color.app_primary')).fontColor(Color.White).borderRadius(25).onClick(()=>{this.toggleTimer();})}

按钮文字和颜色根据isRunning状态动态切换,实现了"开始/暂停"一体化按钮。


六、生命周期清理

aboutToDisappear(){if(this.timer!==-1){clearInterval(this.timer);}}

组件销毁时必须清除定时器,否则会持续消耗资源并可能导致对已销毁组件的状态修改。timer初始值为-1,仅在有活跃定时器时清理。


七、父组件中的使用

TodoView中,倒计时组件的完整使用方式:

timerController:TimerComponentController=newTimerComponentController()@StatesettingTime:number=25*60;onTimerFinished=async()=>{this.alarmVisible=true// 播放提醒音}onTimeChanged=(currentTime:number)=>{console.log(`剩余时间:${currentTime}`);}// 在 build 中使用TimerComponent({totalTime:this.settingTime,onTimeUp:this.onTimerFinished,onTimeChange:this.onTimeChanged,controller:this.timerController})

当用户在弹窗中确认关闭闹钟时,通过控制器重置:

IBestDialog({visible:$alarmVisible,defaultBuilder:():void=>this.alarmBuild(),onConfirm:(()=>{this.alarmVisible=falsethis.timerController.reset()// 远程重置倒计时})})

八、控制器模式的适用场景

场景说明
需要远程重置弹窗确认后重置计时器
需要远程暂停/恢复页面切换时暂停,返回时恢复
需要跨组件通信兄弟组件间通过共享控制器协调行为

控制器模式的扩展方向:

  • 添加更多回调:setPauseCallbacksetResumeCallback
  • 支持多实例:每个控制器绑定一个组件实例
  • 结合@Watch:控制器修改某个@State,子组件通过@Prop @Watch响应

九、小结

控制器模式在 ArkUI 中解决了"父组件主动驱动子组件行为"的需求,其核心三步:

  1. 定义 Controller 类:声明回调属性和注册/执行方法
  2. 子组件注册回调:在aboutToAppearcontroller.setResetCallback(this.method.bind(this))
  3. 父组件调用方法controller.reset()间接触发子组件行为

相比@Link双向绑定,控制器模式更语义化——它表达的是"命令"而非"数据同步",适合倒计时重置、播放器控制、动画触发等命令式交互场景。

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

相关文章:

  • 医疗问答系统Python工程包:含预装Neo4j图库、中文医疗数据与可运行Flask界面
  • 二维码内容压缩与解压:Java工具类实战,解决复杂码扫码难题
  • Grok 4.5政治中立AI模型评测与工程实践指南
  • 基于51/STM32单片机矿井安全 瓦斯检测 天然气 甲烷报警物联网312(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 终极PS4存档管理指南:Apollo Save Tool免费跨平台解决方案
  • (软件实战)单片机串口超时接收:从“9681N”到稳定数据帧的工程实践
  • 基于PCB三维仿真的BMS充电干扰耦合路径诊断与优化
  • Python诗歌分析实战:从格律识别到意象网络构建
  • ROS节点命名机制详解:名称、命名空间与重映射全解析
  • 四次方缩放的工程化破局:用设计师比例替代调参玄学
  • Verilog赋值机制深度解析:从组合逻辑到时序建模的三大核心
  • Camunda入门(一) - 架构选型与核心组件剖析
  • 基于51/STM32单片机老人睡眠质量检测系统 心率体温 翻身检测系统312(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 台州专业打知识产权的律师有哪些? - 品牌排行榜
  • FPGA实战:从蜂鸣器到音频解码芯片的音乐播放系统设计
  • TileGym环境配置与GPU内核编程实践指南
  • 天才的阴影:当‘仅仅不错’遇见‘伟大’——从奥本海默与狄拉克看跨界者的困境
  • 2026 年 7 月新发布:景德镇有实力的肉驴制造企业哪家专业,放下偏见!这种被骂了百年的“笨东西”,正悄悄颠覆你的认知 - 企业官方推荐【认证】
  • Python图像转ASCII艺术:从像素到字符的底层原理与工程实践
  • 微信小程序web-view内嵌H5实战:从零到上线的避坑指南
  • ESP8266驱动ST7789 TFT彩屏:从零配置TFT_eSPI库到图形化界面实战
  • 智能治理成熟度实力榜:2026 年六大数据治理平台,谁真正跑通了 AI?
  • 2026年7月最新苏州宝玑官方售后客服服务电话及地址网点大全 - 亨得利钟表维修中心
  • C++高性能发布-订阅系统设计与实现
  • ggplot2条形图全解析:从数据意图到出版级图表
  • MSP430 GPIO复用机制深度解析:从寄存器配置到低功耗设计实践
  • Numpy系列(八):实战解析FFT在信号处理中的核心应用
  • Matlab 绘图布局全解析:从单图、叠图到子图(figure, plot, subplot, hold on)实战代码
  • Unity游戏前端框架设计:从分层架构到资源管理实战
  • 高质量直播间背景音乐素材精选合集