HarmonyOS《柚兔学伴》项目实战22-TextReader 朗读与语音交互
22. TextReader 朗读与语音交互
本章导读
语音交互是儿童教育类应用的核心能力之一。「柚兔学伴」中,诗词朗读、汉字发音、提醒音效等场景均通过 HarmonyOS SpeechKit 的 TextReader 与第三方 SXPlayer 两套方案实现。本章将详解它们的集成方式与实战用法。

22.1 TextReader 初始化
TextReader 是 HarmonyOS @kit.SpeechKit 提供的系统级朗读控件,使用前必须在 UIAbility 的 onCreate 生命周期中完成初始化。
// EntryAbility.ets
import { TextReader } from '@kit.SpeechKit';
import { BusinessError } from '@ohos.base';
export default class EntryAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
if (this.context) {
const readerParams: TextReader.ReaderParam = {
isVoiceBrandVisible: true,
businessBrandInfo: {
panelName: '小艺朗读',
panelIcon: $r('app.media.startIcon')
}
};
await TextReader.init(this.context, readerParams).then(() => {
console.info(`TextReader succeeded in initializing.`);
}).catch((e: BusinessError) => {
console.error(`TextReader failed to initialize. Code: ${e.code}, message: ${e.message}`);
})
}
}
}
关键参数说明:
| 参数 | 类型 | 作用 |
|---|---|---|
isVoiceBrandVisible |
boolean | 是否在朗读面板显示语音品牌标识 |
businessBrandInfo.panelName |
string | 朗读面板标题,如"小艺朗读" |
businessBrandInfo.panelIcon |
Resource | 朗读面板图标资源 |
初始化必须在 onCreate 中完成,不能延迟到页面加载阶段,否则后续 TextReader.start() 调用会失败。
22.2 PoemReader 朗读工具类
为方便多处复用,项目将 TextReader 的调用封装为静态工具类 PoemReader:
// PoemReader.ets
import { TextReader } from "@kit.SpeechKit";
import { BusinessError } from "@kit.BasicServicesKit";
export class PoemReader {
static play(poem: string, title: string = '', author: string = '', date?: string) {
const readInfoList: TextReader.ReadInfo[] = [{
id: '001',
title: {
text: title,
isClickable: true
},
author: {
text: author,
isClickable: true
},
date: {
text: date ?? new Date().toISOString().split('T')[0],
isClickable: false
},
bodyInfo: poem
}];
const startParams: TextReader.StartParams = {
isMinibarHidden: true,
callbackParam: '0'
};
TextReader.start(readInfoList, undefined, startParams)
.then(() => {
console.info(`TextReader succeeded in starting`);
})
.catch((e: BusinessError) => {
console.error(`TextReader failed to start. Code: ${e.code}, message: ${e.message}`);
});
}
}
ReadInfo 数据结构:
interface ReadInfo {
id: string; // 朗读内容唯一标识
title: { text: string; isClickable: boolean }; // 标题,可点击跳转
author: { text: string; isClickable: boolean }; // 作者,可点击跳转
date: { text: string; isClickable: boolean }; // 日期,不可点击
bodyInfo: string; // 朗读正文内容
}
StartParams 参数:
| 参数 | 说明 |
|---|---|
isMinibarHidden |
是否隐藏迷你朗读条(true 则全屏朗读面板) |
callbackParam |
回调参数,用于标识本次朗读请求 |
设计要点:isClickable 控制朗读面板中对应区域是否可交互。对于诗词场景,标题和作者可点击查看详情,日期设为不可点击。
22.3 三大使用场景
场景一:PoemPage 诗词播读
在诗词详情页,点击"播读"按钮调用朗读:
// PoemPage.ets
@Builder
buildBottom() {
Row() {
Button('播读').layoutWeight(1).borderRadius(5)
.linearGradient({
angle: 90,
colors: [[0xFF33FF, 0.0], [0x8E44FF, 1]]
})
.onClick(() => {
PoemReader.play(this.poemInfo?.poem ?? '');
})
Blank(10)
Button('下一首').layoutWeight(1).borderRadius(5)
.linearGradient({
angle: 90,
colors: [[0x8E44FF, 0.0], [0x1C55FF, 1]]
}).onClick(() => {
this.generatePoem()
})
}.width('100%').padding(10)
}
此处只传入诗词正文,标题、作者使用默认空值。朗读面板会以全屏模式展示。
场景二:StrokeView 汉字发音
在笔画查询页面,点击 Canvas 区域即可听到当前汉字的读音:
// StrokeView.ets
Canvas(this.context!!)
.width(100)
.height(100)
.onReady(() => {
this.canvasReady = true;
if (!this.isLoading) {
this.initializeCanvas();
this.strokeManager.startAnimation(this.context!!, () => {
this.updateAnimationInfo();
});
}
})
.onClick(() => {
PoemReader.play(this.word);
})
this.word 是单个汉字(如"样"),TextReader 会自动识别并朗读该字的发音,非常适合儿童识字场景。
场景三:TodoView 诗词卡片朗读
在首页待办视图中,点击诗词卡片触发朗读:
// TodoView.ets
Column({ space: 12 }) {
Text(this.poem)
.fontFamily('kaiti')
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Center)
.lineSpacing(LengthMetrics.fp(18))
}
.margin({ left: 12 })
.justifyContent(FlexAlign.Center)
.mainCardStyle()
.onClick(() => {
PoemReader.play(this.poem);
})
诗词内容来自每日推荐,用户轻触即可聆听朗读,实现"看诗即听"的沉浸体验。
22.4 SXPlayer 自定义音频播放
对于非朗读类的音频需求(如计时结束提醒音),项目使用 @qtfm/smartxplayer 模块的 SXPlayer:
初始化与回调
// TodoView.ets
import { AudioEntry, PlayActionCallback, PlayParams, SXPlayer } from '@qtfm/smartxplayer';
const context: common.UIAbilityContext = GlobalUIAbilityContext.getContext()
@Component
export struct TodoView {
private callback: PlayActionCallback = {
onPlayPrevious: () => {},
onPlayNext: () => {},
onToggleFavorite: (assetId) => {}
}
private sxplayer: SXPlayer = new SXPlayer(context, {
enableLog: true,
bundleName: "com.youtoo.study.partner",
abilityName: "EntryAbility",
playActionCallback: this.callback
})
}
播放提醒音
倒计时结束时,读取 rawfile 中的铃声文件并播放:
onTimerFinished = async () => {
this.alarmVisible = true
let fileDescriptor = await context.resourceManager.getRawFd("ringtone_youtoo.mp3");
let entity: AudioEntry = { fd: fileDescriptor }
let params: PlayParams = {
audioEntry: [entity],
playWhenPrepared: true,
hasPrevious: false,
hasNext: false,
isLive: false
}
this.sxplayer!.play(params)
}
PlayParams 参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
audioEntry |
AudioEntry[] | 音频数据源,支持 fd 文件描述符 |
playWhenPrepared |
boolean | 准备就绪后自动播放 |
hasPrevious |
boolean | 是否有上一首 |
hasNext |
boolean | 是否有下一首 |
isLive |
boolean | 是否为直播流 |
smartxplayer 模块导出
export { SXPlayer, SXWorkerPlayer, SXBaseAudioPlayer, SXCastPlayer, ISXAudioPlayer }
- SXPlayer:标准播放器,适合短音频播放
- SXWorkerPlayer:Worker 线程播放器,避免阻塞 UI
- SXBaseAudioPlayer:基础播放器抽象类
- SXCastPlayer:投播播放器
- ISXAudioPlayer:播放器接口定义
22.5 两套方案对比与选型建议
| 对比维度 | TextReader | SXPlayer |
|---|---|---|
| 适用场景 | 文本朗读(诗词、汉字) | 音频文件播放(铃声、音乐) |
| 输入类型 | 文本字符串 | 文件描述符(fd) |
| 系统依赖 | SpeechKit | 第三方模块 |
| 朗读面板 | 内置 UI 面板 | 无 UI,纯后台播放 |
| 个性化 | 支持品牌信息定制 | 支持播放控制回调 |
选型原则:
- 需要系统级朗读 UI + 文本转语音 → TextReader
- 需要播放预置音频文件 + 自定义控制逻辑 → SXPlayer
- 两者可共存,互不冲突
本章小结
本章介绍了「柚兔学伴」中语音交互的完整实现方案。TextReader 在 EntryAbility.onCreate 中初始化,通过 PoemReader 工具类在诗词页、笔画页、首页卡片三处统一调用;SXPlayer 则负责计时提醒等纯音频播放场景。两者各司其职,共同构建了应用的语音交互体系。下一章将介绍字帖生成与 PDF 导出功能。
更多推荐

所有评论(0)