简约之美:开发计时器应用——在精确倒计时中感受时间的流转之美(2) 原创
倒计时Gif效果
昨天,我以轻松的姿态投入了编程的战场,为我的计时器应用添上了正计时这一基础功能。而今天,我更是全力以赴,为应用注入了倒计时这一亮点。在这个过程中,我深刻体会到了技术设计的巧妙,简单的代码也能塑造出强大的应用能力。
正计时功能,就像是篮球比赛中的计时器,虽然它在后台默默工作,但它的存在不可或缺。想象你是一位篮球运动员,在每一次训练和比赛中,正计时器记录着你的每一滴汗水,它是你努力付出的见证。
然而,倒计时功能才是这场时间管理游戏的真正主角。想象你站在百米赛跑的起跑线上,倒计时器就像是你心中的发令枪,它提醒你每一刻的重要性和紧迫性。设置一个倒计时,它会在关键时刻响起,确保你能够从容不迫地完成热身,并在枪响的那一刻爆发出全部的力量。
在构建这个计时器时,我们采用了HarmonyOS NEXT的TextTimer组件,它的流畅性让我们的开发过程如鱼得水。以下是我对这次开发的一些心得体会:
正计时器的开发,就像是球场上的即兴表演,快速而直接。用户通过简洁的界面操作,就能轻松管理时间的流逝,让每一秒都变得可控。
倒计时器的开发,则像是精心编排的赛事策略,每一个细节都至关重要。用户可以自定义倒计时的时间,当倒计时结束时,计时器会自动发出提醒,确保用户不会错过任何一个关键时刻。我们不断优化这一功能,力求在用户的每一次使用中,都能感受到便捷与完美。虽然目前我们还未深入挖掘HarmonyOS NEXT的全部潜力,但我相信,随着开发的深入,我们的计时器将变得更加精准和高效。
代码示例
自定义封装弹窗
@CustomDialog
export struct TimerDialog {
controller?: CustomDialogController
selectedTime: Date = initDate();
currentFileName: string = 'TimerDialog'
@State date: TimePickerResult | undefined = undefined
onConfirm: (date: TimePickerResult | undefined) => TimePickerResult | undefined =
(date: TimePickerResult | undefined) => {
return date;
}
onCancle: () => void = () => {
}
build() {
Column() {
Text('设置倒计时')
.fontSize(24)
.margin(30)
TimePicker({
selected: this.selectedTime, format: TimePickerFormat.HOUR_MINUTE_SECOND
})
.useMilitaryTime(true)
.onChange((date: TimePickerResult) => {
this.date = date
console.info('select current date is: ' + JSON.stringify(date))
})
Row() {
Button('确定')
.padding({ left: 30, right: 30 })
.onClick(() => {
this.onConfirm(this.date)
})
Button('取消')
.padding({ left: 30, right: 30 })
.onClick(() => {
this.onCancle()
})
}
.width('100%')
.margin(30)
.justifyContent(FlexAlign.SpaceAround)
}.width('100%')
}
}
export function initDate(): Date {
let date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
return date;
}
ArkTS 交互逻辑
// 自定义弹窗的回调函数及相关参数
dialogController: CustomDialogController = new CustomDialogController({
builder: TimerDialog({
onConfirm: (date: TimePickerResult | undefined) => {
this.dialogController.close()
if (date) {
this.millSeconds = date.hour * 3600000 + date.minute * 60000 + date.second * 1000;
}
return date;
},
onCancle: () => {
animateTo({ duration: 200 }, () => {
this.countMode = !this.countMode;
this.dialogController.close()
})
}
})
})
// UI 交互
Text(this.countMode ? this.countUp : this.countDown)
.fontColor(Color.Blue)
.fontColor(28)
.onClick(() => {
this.countMode = !this.countMode
if (this.countMode) {
this.textTimerController.reset()
}
if (!this.countMode) {
this.millSeconds = 0;
this.dialogController.open()
}
})
.borderWidth({ bottom: 1 })
通过触发弹窗的 open() 方法,我们展示了弹窗界面供用户进行选择。一旦用户选择了日期,我们便可以利用回调函数来执行毫秒级的换算操作,进而更新 TextTimer 倒计时组件,确保其显示与用户所选日期同步。
核心UI结构
@Component
@Entry
export struct ClockDemo1 {
textTimerController: TextTimerController = new TextTimerController()
@State countMode: boolean = true
countUp: string = '正计时 => 倒计时';
countDown: string = '倒计时 => 正计时'
@State millSeconds: number = -1;
dialogController: CustomDialogController = new CustomDialogController({
builder: TimerDialog({
onConfirm: (date: TimePickerResult | undefined) => {
this.dialogController.close()
if (date) {
this.millSeconds = date.hour * 3600000 + date.minute * 60000 + date.second * 1000;
}
return date;
},
onCancle: () => {
animateTo({ duration: 200 }, () => {
this.countMode = !this.countMode;
this.dialogController.close()
})
}
})
})
build() {
Column() {
Text(this.countMode ? this.countUp : this.countDown)
.fontColor(Color.Blue)
.fontColor(28)
.onClick(() => {
this.countMode = !this.countMode
if (this.countMode) {
this.textTimerController.reset()
}
if (!this.countMode) {
this.millSeconds = 0;
this.dialogController.open()
}
})
.borderWidth({ bottom: 1 })
Row() {
if (this.countMode) {
TextTimer({
controller: this.textTimerController,
})
.fontSize(120)
.format('HH:mm:ss')
} else {
TextTimer({
controller: this.textTimerController,
count: this.millSeconds,
isCountDown: true
})
.fontSize(120)
.format('HH:mm:ss')
}
}
.borderWidth(1)
.borderRadius(30)
Row() {
TextButton({ text: '启动' })
.onClick(() => {
this.textTimerController.start()
})
TextButton({ text: '暂停' })
.onClick(() => {
this.textTimerController.pause()
})
TextButton({ text: '重置' })
.onClick(() => {
this.textTimerController.reset()
})
}
.width('50%')
.justifyContent(FlexAlign.SpaceEvenly)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.SpaceEvenly)
}
}
@Component
struct TextButton {
text: string = '文本按钮'
build() {
Row() {
Text(this.text)
.margin(5)
}
.borderWidth(1)
.width(80)
.borderRadius(100)
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
}
}
@CustomDialog
export struct TimerDialog {
controller?: CustomDialogController
selectedTime: Date = initDate();
currentFileName: string = 'TimerDialog'
@State date: TimePickerResult | undefined = undefined
onConfirm: (date: TimePickerResult | undefined) => TimePickerResult | undefined =
(date: TimePickerResult | undefined) => {
return date;
}
onCancle: () => void = () => {
}
build() {
Column() {
Text('设置倒计时')
.fontSize(24)
.margin(30)
TimePicker({
selected: this.selectedTime, format: TimePickerFormat.HOUR_MINUTE_SECOND
})
.useMilitaryTime(true)
.onChange((date: TimePickerResult) => {
this.date = date
console.info('select current date is: ' + JSON.stringify(date))
})
Row() {
Button('确定')
.padding({ left: 30, right: 30 })
.onClick(() => {
this.onConfirm(this.date)
})
Button('取消')
.padding({ left: 30, right: 30 })
.onClick(() => {
this.onCancle()
})
}
.width('100%')
.margin(30)
.justifyContent(FlexAlign.SpaceAround)
}.width('100%')
}
}
export function initDate(): Date {
let date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
return date;
}