#星计划#梅科尔工作室HOS-鸿蒙开发实战(深呼吸) 原创
页面整体展示效果
源码
import http from '@ohos.net.http'
@Entry
@Component
struct jieya {
textTimerController: TextTimerController = new TextTimerController()
@State isTimerRunning: boolean = true // 新增一个状态变量来表示计时器运行状态
@State format: string = 'HH:mm:ss'
@State audioSrc: Resource = $rawfile("shuhuanyinyue.mp3"); //音频路径
@State isAutoPlay: boolean = false; //是否自动播放
@State autoPlay:boolean = false;//是否自动播放
@State loop:boolean = true;//是否循环播放
@State huxicishu: number=0
videoController: VideoController = new VideoController();//导入对象
@State textValue: string = ''
@State inputValue: string = 'click me'
@State hours : number=0
@State minutes : number=0
@State seconds : number=0
controller: TextClockController = new TextClockController()
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample({
cancel: this.onCancel,
confirm: this.onAccept,
hours:$hours,
minutes:$minutes,
seconds:$seconds,
huxicishu:$huxicishu,
}),
cancel: this.existApp,
autoCancel: true,
alignment: DialogAlignment.Center,
offset: { dx: 0, dy: -20 },
gridCount: 3,
customStyle: false
})
// 在自定义组件即将析构销毁时将dialogController置空
aboutToDisappear() {
this.dialogController = undefined // 将dialogController置空
}
onCancel() {
console.info('Callback when the first button is clicked')
}
onAccept() {
console.info('Callback when the second button is clicked')
}
existApp() {
console.info('Click the callback in the blank area')
}
build(){
Row() {
Column() {
Stack() {
Image($r('app.media.huxibeijing'))
Column({space:40}) {
Stack() {
Column()
.height(180)
.width(180)
.borderRadius(90)
.backgroundColor("#FFFFFF")
.opacity(0.71)
TextTimer({ controller: this.textTimerController, isCountDown: false, count: 30000 })
.format(this.format)
.fontColor(Color.Black)
.fontSize(35)
.onTimer((utc: number, elapsedTime: number) => {
console.info('textTimer notCountDown utc is:' + utc + ', elapsedTime: ' + elapsedTime)
this.huxicishu=parseInt((this.seconds/5).toString())
this.hours = parseInt((elapsedTime % (1000 * 60 * 60 * 24) / (1000 * 60 * 60)).toString());
this.minutes = parseInt((elapsedTime % (1000 * 60 * 60) / (1000 * 60)).toString());
this.seconds = parseInt((elapsedTime% (1000 * 60) / 1000).toString());
})
}
Video({
src: this.audioSrc,
controller: this.videoController
}).height(0)
.autoPlay(this.isAutoPlay)
Column({space:20}) {
Button("开始呼吸")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.textTimerController.start()
this.videoController.start();
this.controller.start()
})
Button("结束呼吸")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.textTimerController.reset()
this.videoController.stop()
this.controller.stop()
if (this.dialogController != undefined) {
this.dialogController.open()
}
})
Button("历史记录")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.textTimerController.pause()
this.videoController.pause();
})
}
}.width("100%").height("100%")
}
} } }}
项目架构
main
├─ module.json5
├─ resources
│ ├─ zh_CN
│ ├─ media //图片资源目录
│ ├─ rawfile //音频资源目录
│ └─ base
└─ ets
├─ pages
│ └─ Index.ets //主页面
└─ entryability
└─ EntryAbility.ts
1.背景音乐的设置
我们使用官方用于播放视频文件并控制其播放状态的组件Video进行开发
首先进行变量的声明
@State videoSrc: Resource = $rawfile("shuhuanyinyue.mp3"); //音频路径
@State isAutoPlay: boolean = false; //是否自动播放
@State autoPlay:boolean = false;//是否自动播放
@State loop:boolean = true;//是否循环播放
导入对象
videoController: VideoController = new VideoController();
使用Video组件,设置其高度为0,路径为声明的音频路径
Video({
src: this.audioSrc,
controller: this.videoController
}).height(0)
.autoPlay(this.isAutoPlay)
点击开始呼吸按钮,背景音乐开始播放,点击结束按钮,背景音乐停止
Column({space:20}) {
Button("开始呼吸")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.videoController.start();
})
Button("结束呼吸")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.videoController.stop()
})
}
2.计时器的使用
首先声明format变量,计时器以小时、分钟、秒的形式进行计时
@State format: string = 'HH:mm:ss'
导入计时器对象
textTimerController: TextTimerController = new TextTimerController()
使用TextTimer组件显示计时器信息并控制计时器的状态
TextTimer({ controller: this.textTimerController, isCountDown: false, count: 30000 })
.format(this.format)
.fontColor(Color.Black)
.fontSize(35)
.onTimer((utc: number, elapsedTime: number) => {
console.info('textTimer notCountDown utc is:' + utc + ', elapsedTime: ' + elapsedTime)
})
点击开始呼吸按钮,计时器开始计时,点击结束呼吸按钮,计时器结束计时并清0
Button("开始呼吸")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.textTimerController.start()
})
Button("结束呼吸")
.width(150)
.height(50)
.fontColor("#636B60")
.fontSize(18)
.backgroundColor("white")
.borderRadius(15)
.onClick(() => {
this.textTimerController.reset()
})
3.自定义弹窗
在父组件中进行对话框的构建、显示和控制以及对话框内的文本内容和状态的更新。
controller: TextClockController = new TextClockController()
dialogController: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample({
cancel: this.onCancel,
confirm: this.onAccept,
textValue: $textValue,
inputValue: $inputValue,
cot:$cot,
hours:$hours,
minutes:$minutes,
seconds:$seconds,
milliseconds:$milliseconds,
huxicishu:$huxicishu,
}),
cancel: this.existApp,
autoCancel: true,
alignment: DialogAlignment.Center,
offset: { dx: 0, dy: -20 },
gridCount: 3,
customStyle: false
})
在子组件进行弹窗UI页面的编写,代码如下
@CustomDialog
struct CustomDialogExample {
@Link huxicishu: number
@Link hours : number
@Link minutes : number
@Link seconds : number
controller: CustomDialogController
// 若尝试在CustomDialog中传入多个其他的Controller,以实现在CustomDialog中打开另一个或另一些CustomDialog,那么此处需要将指向自己的controller放在最后
cancel: () => void
confirm: () => void
build(){
Stack(){
Image($r("app.media.tanchuangbeijing")).height(290).borderRadius(20)
Column({space:12}){
Row(){
Image($r("app.media.huxijieguo")).height(45).width(50)
Text("呼吸结果").fontSize(22)
}
Text("恭喜你又完成一次呼吸!").fontSize(12).fontColor("#4C5250")
Divider().vertical(false).strokeWidth(1).color("#BBBBBB").lineCap(LineCapStyle.Round).width(200)
Row(){
Image($r("app.media.huxicishu")).height(40).width(36)
Text("呼吸次数:").fontSize(14)
Text(this.huxicishu.toString()).fontSize(22).fontColor("#C21818")
Text("次").fontSize(14)
}
Row(){
Image($r("app.media.huxishichang")).height(40).width(36)
Text("呼吸时长:").fontSize(14)
Text(this.hours.toString()).fontSize(22).fontColor("#C21818")
Text("h").fontSize(18)
Text(this.minutes.toString()).fontSize(22).fontColor("#C21818")
Text("min").fontSize(18)
Text(this.seconds.toString()).fontSize(22).fontColor("#C21818")
Text("s").fontSize(19)
}
Flex({ justifyContent: FlexAlign.SpaceAround }) {
Button('我知道了')
.onClick(() => {
this.controller.close()
this.cancel()
}).backgroundColor("#59C8FD").fontColor(Color.White).height(35).width(128)
}
} } }}
4.将计时器计时到的数据传到弹窗界面
首先使用@State声明变量并进行初始化
@State hours : number=0
@State minutes : number=0
@State seconds : number=0
获取计时器计时的时间 elapsedTime,单位为毫秒。然后将该时间转换成小时分钟秒的形式。
this.hours = parseInt((elapsedTime % (1000 * 60 * 60 * 24) / (1000 * 60 * 60)).toString());
this.minutes = parseInt((elapsedTime % (1000 * 60 * 60) / (1000 * 60)).toString());
this.seconds = parseInt((elapsedTime% (1000 * 60) / 1000).toString());
子组件使用@Link装饰器将hours、minutes、seconds、milliseconds这些状态变量与父组件传递的相应变量进行关联,以便在对话框中显示和更新这些值。
@Link hours : number
@Link minutes : number
@Link seconds : number
在子组件弹窗组件对应位置对计时到的数据进行调用
Row(){
Image("/common/images/huxishichang.png").height(40).width(36)
Text("呼吸时长:").fontSize(14)
Text(this.hours.toString()).fontSize(22).fontColor("#C21818")
Text("h").fontSize(18)
Text(this.minutes.toString()).fontSize(22).fontColor("#C21818")
Text("min").fontSize(18)
Text(this.seconds.toString()).fontSize(22).fontColor("#C21818")
Text("s").fontSize(19)
}
5.总结
该页面使用鸿蒙开发的主力语言——ArkTS语言进行开发,完成了页面的布局、元素控制以及逻辑控制等。该实践内容主要供用户进行解压放松,点击开始呼吸按钮,开始播放背景音乐,用户深呼吸进行放松,同时计时器也开始计时,点击结束呼吸按钮,背景音乐和计时器都停止,同时自定义弹窗出现,该弹窗展示了用户本次呼吸时长以及呼吸次数等。
本次开发是一次很不错的体验,使我了解了ArkTS语言在鸿蒙开发中以其独特的特点和优势。后续我也会不断学习和探索,也期待着与更多的开发者一起共同探讨和实践鸿蒙开发技术。