学习例程6.1:HarmonyOS 鸿蒙Next 一个带语音的故事版。
学习例程6.1:HarmonyOS 鸿蒙Next 一个带语音的故事版。 想写个有点意思,既能给孩子玩又能有点儿教育意义的小游戏。
琢磨半天,可以让游戏附带个故事版,讲几句历史故事。
雏形如下。先上图。
代码如下:
import { media } from "@kit.MediaKit"
import { common } from "@kit.AbilityKit"
import { audio } from "@kit.AudioKit"
@ComponentV2
@Entry
export struct testStoryBoard {
build() {
Column() {
ViewStoryBoard()
}
}
}
@ComponentV2
@Preview
export struct ViewStoryBoard {
@Local nowStoryFrame: StoryBoardFrame | undefined = undefined
@Local nowStoryUnit: StoryBoardUnit | undefined = undefined
@Local headOpacity: number = 0.1
buildTest() {
this.nowStoryFrame = new StoryBoardFrame()
this.nowStoryFrame.backPicture = $r("app.media.huarongdao")
let roleCaocao = new Role("曹操", "魏国丞相", $r("app.media.caocaoHeadRight"))
let roleZhaoyun = new Role("赵云", "蜀国大将", $r("app.media.zhaoyun_head"))
let unitCaocao = new StoryBoardUnit()
unitCaocao.name = "曹操说"
unitCaocao.role = roleCaocao
unitCaocao.words = "呵呵!吾不笑别人,单笑周瑜无谋,诸葛亮少智。\n若是吾用兵之时,预先在这里伏下一军,如之奈何?"
unitCaocao.sound = "caocao_01.mp3"
this.nowStoryFrame.units.push(unitCaocao)
let unitZhaoyun = new StoryBoardUnit()
unitZhaoyun.name = "赵云说"
unitZhaoyun.role = roleZhaoyun
unitZhaoyun.words = "我赵子龙奉军师将令,在此等候多时了!"
unitZhaoyun.sound = "zhaoyun_01.mp3"
this.nowStoryFrame.units.push(unitZhaoyun)
this.playOneUnit(this.nowStoryFrame, 0)
}
async playOneUnit(tframe: StoryBoardFrame, idx: number) {
this.headOpacity = 0.1
this.nowStoryUnit = tframe.units[idx]
console.error("playonunit", this.nowStoryUnit.words)
if (tframe.units[idx].sound != undefined) {
let soundRaw = tframe.units[idx].sound as string
let avPlayer: media.AVPlayer = await media.createAVPlayer();
// 创建状态机变化回调函数
avPlayer.on('durationUpdate', (duration: number) => {
console.info('durationUpdate called,时长:' + duration)
})
avPlayer.on('timeUpdate', (times) => {
console.info('timeUpdate', times)
if (times > 100) {
this.headOpacity = 1
avPlayer.off('timeUpdate')
}
})
console.error("注册 stateChange")
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
switch (state) {
case 'idle': // 成功调用reset接口后触发该状态机上报
console.info('AVPlayer state idle called.');
avPlayer.release(); // 调用release接口销毁实例对象
break;
case 'initialized': // avplayer 设置播放源后触发该状态上报
console.info('AVPlayer state initialized called.');
avPlayer.audioRendererInfo = {
usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
rendererFlags: 0
};
avPlayer.prepare();
break;
case 'prepared': // prepare调用成功后上报该状态机
console.info('准备好了AVPlayer state prepared called.');
avPlayer.play(); // 调用播放接口开始播放
break;
case "completed":
console.error(`${soundRaw}播放完毕!`)
if (idx < tframe.units.length - 1) {
this.playOneUnit(tframe, idx + 1)
}
avPlayer.reset(); // 调用reset接口初始化avplayer状态
break
}
})
// 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
// 返回类型为{fd,offset,length},fd为HAP包fd地址,offset为媒体资源偏移量,length为播放长度
let context = getContext(this) as common.UIAbilityContext;
let fileDescriptor = await context.resourceManager.getRawFd(soundRaw);
console.error(`[${soundRaw}]资源id:`, fileDescriptor.fd)
let avFileDescriptor: media.AVFileDescriptor =
{ fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
// 为fdSrc赋值触发initialized状态机上报
avPlayer.fdSrc = avFileDescriptor;
}
}
aboutToAppear(): void {
this.buildTest()
}
build() {
Stack() {
Image(this.nowStoryFrame?.backPicture)
.opacity(1)
Stack() {
Row() {}
.width('100%')
.backgroundColor('black')
.opacity(0.8)
.height(80)
Row() {
Column() {
Image(this.nowStoryUnit?.role?.headPicture)
.width("20%")
.backgroundColor("#aaaaaa")
.borderRadius('50%')
.animation({ curve: Curve.Ease })
.opacity(this.headOpacity)
Text(this.nowStoryUnit?.role?.name)
.fontColor("#aabbcc")
.fontSize(10)
}
if (this.nowStoryUnit?.words) {
ViewTxtShow({ Words: this.nowStoryUnit?.words })
.width('80%')
.margin(5)
}
}
}.width('100%')
.alignContent(Alignment.Start)
}
.width('100%')
.height('300')
.alignContent(Alignment.BottomStart)
}
}
@ObservedV2
export class StoryBoardUnit {
public name: string = ""
public role: Role | undefined = undefined
@Trace
public words: string = ""
public duration: number = 2000
public sound: string | undefined = undefined
public picture: Resource | string = ""
}
@ObservedV2
export class StoryBoardFrame {
public units: StoryBoardUnit[] = []
public backPicture: Resource | string = ""
public Index: number = -1
}
@ObservedV2
export class Role {
public name: string = "关羽"
public introduce: string = "三国时期,蜀国大将。"
public headPicture: Resource | string = ""
constructor(tName: string, tIntroduce: string, tHeadPic: Resource | string) {
this.name = tName
this.introduce = tIntroduce
this.headPicture = tHeadPic
}
}
@ComponentV2
@Preview
export struct ViewTxtShow {
@Param Words: string = "哈哈!人言周瑜、诸葛亮足智多谋,我看到底是无能之辈。若在此处埋伏一军,我等皆束手受缚矣。"
@Monitor("Words")
infoChange(monitor: IMonitor) {
console.warn(`Words 已修改为`, this.Words);
this.buidList()
this.buildStepShow()
}
@Local wordList: string[] = []
@Local wordOpacity: number[] = []
buidList() {
console.warn("BuildList")
this.wordList = []
this.wordOpacity = []
for (let i = 0; i < this.Words.length; i++) {
this.wordList[i] = this.Words[i]
this.wordOpacity[i] = 0.3
}
}
buildStepShow(idx: number = 0) {
setTimeout(() => {
this.wordOpacity[idx] = 1;
if (idx < this.wordOpacity.length - 1) {
let tidx = idx + 1;
this.buildStepShow(tidx)
}
}, 301)
}
aboutToAppear(): void {
console.warn("viewTxtShow aboutToAppear", this.Words)
this.buidList()
this.buildStepShow()
}
onDidBuild(): void {
console.warn("viewTxtShow onDidBuild", this.Words)
}
build() {
Column() {
if (this.Words)
Grid() {
ForEach(this.wordList, (char: string, idx: number) => {
GridItem() {
Text(this.Words[idx])
.opacity(this.wordOpacity[idx])
.animation({ curve: Curve.Ease })
.fontColor('white')
}
}, (item: string) => item)
}
.direction(Direction.Auto)
.width("100%")
}
}
}
记录。分享。睡午觉。
更多关于学习例程6.1:HarmonyOS 鸿蒙Next 一个带语音的故事版。的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于学习例程6.1:HarmonyOS 鸿蒙Next 一个带语音的故事版。的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,开发一个带语音的故事版应用,主要涉及以下几个技术点:
-
语音功能集成:使用鸿蒙系统提供的
@ohos.multimedia.audio
模块来实现语音播放功能。通过AudioPlayer
类可以加载音频文件并进行播放控制,如播放、暂停、停止等操作。 -
UI设计与布局:利用
@ohos.arkui
模块中的Component
和Layout
来构建用户界面。可以通过Text
组件显示故事文本,Button
组件实现播放控制按钮,Image
组件展示故事插图等。 -
事件处理:通过
@ohos.arkui
中的Event
模块来处理用户交互事件,如点击按钮时触发语音播放或暂停操作。 -
资源管理:将音频文件、图片等资源放置在
resources
目录下,并通过$r
或$rawfile
进行引用。 -
生命周期管理:在
EntryAbility
中管理应用的生命周期,确保在应用启动时加载必要的资源,并在应用退出时释放资源。 -
调试与测试:使用
DevEco Studio
进行代码编写、调试和测试,确保应用的功能和性能达到预期。
通过以上技术点的结合,可以开发出一个功能完善的带语音的故事版应用。