【HarmonyOS AI赋能】朗读控件详解

作者:GeorgeGcs日期:2025/10/13

【HarmonyOS AI赋能】朗读控件详解

一、前言

鸿蒙系统提供了系统级别的朗读控件,来实现对文本进行朗读的业务需求。不需要复杂的SDK接入和集成,就可实现商业级别的朗读效果。

朗读控件分为听筒组件朗读控制器,以及朗读面板三部分组成。 朗读面板又分为吸边小面板全屏朗读面板

需要注意的是,仅支持中国境内(不包含中国香港、中国澳门、中国台湾)提供服务。并且实时朗读的正文信息长度10000字符以内。请添加图片描述

二、如何使用朗读控件?

以下代码为上图所示的DEMO源码,可直接新建工程后,贴到index.ets类中,启自动签名后,启动查看效果。下面为大家详细拆解如何使用。

1// 导入语音朗读相关的组件和类型
2import { TextReader, TextReaderIcon, ReadStateCode } from '@kit.SpeechKit';
3
4@Entry
5@Component
6struct Index {
7
8  /**
9   * 待加载的文章列表
10   */
11  @State readInfoList: TextReader.ReadInfo[] = [];
12
13  /**
14   * 当前选中的文章
15   */
16  @State selectedReadInfo: TextReader.ReadInfo = this.readInfoList[0];
17
18  /**
19   * 朗读状态
20   */
21  @State readState: ReadStateCode = ReadStateCode.WAITING;
22
23  /**
24   * 初始化状态标记
25   */
26  @State isInit: boolean = false;
27
28  // 组件即将显示时触发
29  async aboutToAppear(){
30    /**
31     * 模拟加载文章数据
32     */
33    let readInfoList: TextReader.ReadInfo[] = [{
34      id: '001',
35      title: {
36        text:'水调歌头.明月几时有',
37        isClickable:true
38      },
39      author:{
40        text:'宋.苏轼',
41        isClickable:true
42      },
43      date: {
44        text:'2024/01/01',
45        isClickable:false
46      },
47      bodyInfo: '明月几时有?把酒问青天。不知天上宫阙,今夕是何年?'
48    }];
49
50    // 更新状态变量
51    this.readInfoList = readInfoList;
52    this.selectedReadInfo = this.readInfoList[0];
53
54    // 初始化朗读组件
55    this.init();
56  }
57
58  /**
59   * 初始化朗读组件
60   */
61  async init() {
62    // 朗读参数配置
63    const readerParam: TextReader.ReaderParam = {
64      isVoiceBrandVisible: true, // 显示品牌信息
65      businessBrandInfo: {
66        panelName: '小艺朗读', // 面板名称
67        panelIcon: $r('app.media.startIcon') // 面板图标
68      }
69    }
70
71    try {
72      // 获取上下文
73      let context: Context | undefined = this.getUIContext().getHostContext()
74      if (context) {
75        // 初始化朗读组件
76        await TextReader.init(context, readerParam);
77        this.isInit = true; // 标记初始化完成
78        this.setActionListener(); // 设置事件监听
79      }
80    } catch (err) {
81      // 初始化失败时打印错误信息
82      console.error([`TextReader failed to init. Code: ${err.code}, message: ${err.message}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.code.md));
83    }
84  }
85
86  // 设置朗读事件监听
87  setActionListener() {
88    // 监听朗读状态变化
89    TextReader.on('stateChange', (state: TextReader.ReadState) => {
90      this.onStateChanged(state);
91    });
92
93    // 监听加载更多请求
94    TextReader.on('requestMore', () => {
95      TextReader.loadMore([], true);
96    })
97  }
98
99  // 处理朗读状态变化
100  onStateChanged = (state: TextReader.ReadState) => {
101    // 只处理当前选中文章的状态变化
102    if (this.selectedReadInfo?.id === state.id) {
103      this.readState = state.state;
104    } else {
105      this.readState = ReadStateCode.WAITING;
106    }
107  }
108
109  // 构建UI界面
110  build() {
111    Column() {
112      // 朗读状态图标
113      TextReaderIcon({ readState: this.readState })
114        .margin({ right: 20 })
115        .width(32)
116        .height(32)
117        .onClick(async () => {
118          // 点击图标时开始朗读
119          try {
120            await TextReader.start(this.readInfoList, this.selectedReadInfo?.id);
121          } catch (err) {
122            // 朗读失败时打印错误信息
123            console.error([`TextReader failed to start. Code: ${err.code}, message: ${err.message}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.code.md));
124          }
125        })
126    }
127    .height('100%')
128  }
129}
130

(1)听筒控件TextReaderIcon提供的听筒控件,可以同步朗读状态,如上动态图所示,有现成的朗读效果,如果业务需要使用,可以用。或者直接跳过也可以,控件参数比较简单,如下代码所示:

1 TextReaderIcon({ readState: this.readState })
2        .width(32)
3        .height(32)
4        .onClick(async () => {
5					// do something...
6        })
7

readState 需要通过朗读控制器TextReader去监听,当前的朗读状态,然后设置给朗读控件,就可以实现朗读控件的动态效果。

1    TextReader.on('stateChange', (state: TextReader.ReadState) => {
2			
3    });
4

并且根据DEMO代码可发现,听筒控件的点击事件,触发了朗读控制器对象的开启操作。

综上所述,我们可以不使用话筒控件,直接使用朗读控制器,调用其接口实现文本朗读的效果。

(2)朗读控制器TextReaderTextReader是整个朗读操作逻辑的核心操作对象,系统接口提供了该单例对象。使用之前需要先初始化:

1    // 朗读参数配置
2    const readerParam: TextReader.ReaderParam = {
3      isVoiceBrandVisible: true, // 显示品牌信息
4      businessBrandInfo: {
5        panelName:  '朗读', // 面板名称
6      },
7      isMinibarNeeded: true
8    }
9    await TextReader.init(context, readerParam);
10

然后再进行常规的启动,暂停(pause),销毁暂停(stop)【ps: 我现在对系统接口,这种类似双暂停的命名很无语 = =。猛地看起来,两个暂停,傻傻分不清楚。但是目前stop后者,多用于整个生命周期回收重置的调用处理。】:

1 // 朗读启动配置
2    const startParams: TextReader.StartParams = {
3      isMinibarHidden: this.mTextReaderInitData?.isMinibarNeeded ?? true,
4    }
5    // 填充朗读内容
6    let readInfoList: TextReader.ReadInfo[] = [{
7        id: '002',
8        title: {
9          text:'水调歌头.明月几时有2',
10          isClickable:true
11        },
12        author:{
13          text:'宋.苏轼2',
14          isClickable:true
15        },
16        date: {
17          text:'2025/02/02',
18          isClickable:false
19        },
20        bodyInfo: '2明月几时有?把酒问青天。不知天上宫阙,今夕是何年?'
21      }];
22    // 启动朗读
23    await TextReader.start(readInfoList, this.readInfoList[0].id, startParams);
24

再之后进行根据业务需求,做一些监听和反监听的处理了,种类很多详情参见api接口:

1    TextReader.on('stateChange', (state: TextReader.ReadState) => {
2
3    });
4

(3)朗读面板关于朗读面板,我理解是通过子窗口来实现,吸边小面板和全屏面板的效果。因为文档中有强调使用朗读控件初始化前,需要使用windowManager进行舞台窗口对象的注入(WindowManager.setWindowStage(windowStage);):

1import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
2import { window } from '@kit.ArkUI';
3import { WindowManager } from '@kit.SpeechKit';
4
5
6export default class EntryAbility extends UIAbility {
7  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
8    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
9
10  }
11
12  onWindowStageCreate(windowStage: window.WindowStage): void {
13
14    WindowManager.setWindowStage(windowStage);
15    windowStage.loadContent('pages/Index', (err) => {
16      if (err.code) {
17        return;
18      }
19    });
20  }
21
22}
23

在这里插入图片描述但是在实际使用中我发现,即使不调用该注入方法。初始化也不会报错。目前已提交工单,后续结论同步到该文章。

朗读面板的操作逻辑很简单,分别提供了显示和隐藏两个面板(吸边小面板和全屏面板)的属性或者接口,来控制显隐。

吸边小面板可通过属性和方法分别设置显隐:

1首先是在初始化配置参数中:
2    const readerParam: TextReader.ReaderParam = {
3      isMinibarNeeded:  true
4    }
5    
6其次是在启动配置参数中:
7    const startParams: TextReader.StartParams = {
8      isMinibarHidden:  true,
9    }
10    
11再之后就是方法接口:
12    TextReader.showMinibar();
13    TextReader.hideMinibar();
14

全屏朗读面板默认启动朗读后就会显示,系统提供了两套接口,可以在start后调用hide就可隐藏全屏朗读面板:

1    TextReader.hidePanel();
2    TextReader.showPanel();
3
1    await TextReader.start(readInfoList, this.mCurrentReadInfo.id);
2    TextReader.hidePanel();
3

三、工具类封装源码共享:

封装ReaderIconView朗读图标,联动管理类的朗读状态,即插即用。

1import { ReadStateCode, TextReaderIcon } from "@kit.SpeechKit";
2import { TextReaderMgr, TextReaderRegister } from "../mgr/TextReaderMgr";
3import { common } from "@kit.AbilityKit";
4
5@Component
6export struct ReaderIconView {
7  private TAG: string = "ReaderIconView";
8
9  /**
10   * 朗读状态
11   */
12  @State readState: ReadStateCode = ReadStateCode.WAITING;
13
14  private mTextReaderRegister: TextReaderRegister = {
15    onStateChange: (state: ReadStateCode): void => {
16      this.readState = state;
17      console.log(this.TAG, "mTextReaderRegister onStateChange state: " + state);
18    }
19  }
20
21  aboutToAppear(): void {
22    const context = getContext(this) as common.UIAbilityContext;
23    TextReaderMgr.Ins().initReader(context, null, null, this.mTextReaderRegister);
24    console.log(this.TAG, " aboutToAppear initReader done");
25  }
26
27
28  build() {
29    TextReaderIcon({ readState: this.readState })
30      .width("100%")
31      .height("100%")
32  }
33
34}
35

封装单例朗读管理类,用于便捷操作朗读相关接口,封装细节,方便快速调用:

1import { ReadStateCode, TextReader } from "@kit.SpeechKit";
2
3/**
4 * 初始化配置对象
5 */
6export class TextReaderInitData {
7  // 全屏面板标题名称
8  panelName: string = "";
9  // 是否需要吸边小面板
10  isMinibarNeeded: boolean = true;
11  // 是否需要全屏面板
12  isPanelNeeded: boolean = true;
13}
14
15/**
16 * 控制器操作回调
17 */
18export interface TextReaderCall {
19  onReady: () => void
20  onInitFail: (err: string) => void
21  onFail: (err: string) => void
22}
23
24/**
25 * 监听回调
26 */
27export interface TextReaderRegister {
28  onStateChange: (state: ReadStateCode) => void
29}
30
31/**
32 * 错误码
33 */
34export enum TextReaderFail {
35  UnInit = "0",
36  TextReaderInfoNULL = "1"
37}
38
39/**
40 * 文本朗读对象
41 */
42export class TextReaderInfo {
43  title: string = "";
44  content: string = "";
45  author?: string = "";
46  date?: string = "";
47}
48
49/**
50 * 文本朗读管理类
51 */
52export class TextReaderMgr {
53  private TAG: string = "TextReaderMgr";
54  private static mTextReaderMgr: TextReaderMgr | null = null;
55  private mInit: boolean = false;
56  private mTextReaderCall: TextReaderCall | null = null;
57  private mTextReaderInitData: TextReaderInitData | null = null;
58  private mTextReaderRegister: TextReaderRegister | null = null;
59
60  private mCurrentReadInfo: TextReader.ReadInfo | null = null;
61
62  public static Ins() {
63    if (!TextReaderMgr.mTextReaderMgr) {
64      TextReaderMgr.mTextReaderMgr = new TextReaderMgr();
65    }
66    return TextReaderMgr.mTextReaderMgr;
67  }
68
69  /**
70   * 设置朗读事件监听
71   */
72  private setActionListener() {
73    // 监听朗读状态变化
74    TextReader.on('stateChange', (state: TextReader.ReadState) => {
75      let readState: ReadStateCode = ReadStateCode.WAITING;
76      if (this.mCurrentReadInfo?.id === state.id) {
77        readState = state.state;
78      } else {
79        readState = ReadStateCode.WAITING;
80      }
81      this.mTextReaderRegister?.onStateChange(readState);
82    });
83
84    // 监听加载更多请求
85    TextReader.on('requestMore', (callbackStr) => {
86      console.log(this.TAG, " callbackStr: " + callbackStr);
87      let readInfoList: TextReader.ReadInfo[] = [{
88        id: '002',
89        title: {
90          text: '水调歌头.明月几时有2',
91          isClickable: true
92        },
93        author: {
94          text: '宋.苏轼2',
95          isClickable: true
96        },
97        date: {
98          text: '2025/02/02',
99          isClickable: false
100        },
101        bodyInfo: '2明月几时有?把酒问青天。不知天上宫阙,今夕是何年?'
102      }];
103      TextReader.loadMore(readInfoList, true);
104    })
105  }
106
107  /**
108   * 初始化朗读播放控件
109   */
110  public async initReader(context: Context, callback?: TextReaderCall | null, data?: TextReaderInitData | null,
111    register?: TextReaderRegister) {
112    this.mTextReaderCall = callback ?? null;
113    this.mTextReaderInitData = data ?? null;
114    this.mTextReaderRegister = register ?? null;
115
116    // 朗读参数配置
117    const readerParam: TextReader.ReaderParam = {
118      isVoiceBrandVisible: data?.panelName == "" ? false : true ?? true, // 显示品牌信息
119      businessBrandInfo: {
120        panelName: data?.panelName == "" ? '朗读' : data?.panelName ?? '朗读', // 面板名称
121      },
122      isMinibarNeeded: data?.isMinibarNeeded ?? true
123    }
124
125    try {
126      if (context) {
127        // 初始化朗读组件
128        await TextReader.init(context, readerParam);
129        this.mInit = true; // 标记初始化完成
130        this.setActionListener(); // 设置事件监听
131        this.mTextReaderCall?.onReady();
132      }
133    } catch (err) {
134      // 初始化失败时打印错误信息
135      console.error(this.TAG, [`TextReader failed to init. Code: ${err.code}, message: ${err.message}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.code.md));
136      this.mTextReaderCall?.onInitFail(JSON.stringify(err));
137    }
138  }
139
140  /**
141   * 文本朗读播放接口(不显示字幕全屏面板和吸边小面板,直接朗读文本)
142   * @param content 实时朗读的正文信息(长度10000字符以内)
143   */
144  public async startContent(context: Context, content: string) {
145    await this.initReader(context);
146    let readInfoList: TextReader.ReadInfo[] = [{
147      id: '0',
148      title: {
149        text: '',
150        isClickable: true
151      },
152      bodyInfo: content
153    }];
154    this.mCurrentReadInfo = readInfoList[0];
155    await TextReader.start(readInfoList, this.mCurrentReadInfo.id);
156    TextReader.hidePanel();
157  }
158
159  /**
160   * 启动朗读
161   * @param infoArr
162   */
163  public async start(infoArr: TextReaderInfo[]) {
164    // 判断当前是否初始化成功过
165    if (!this.mInit) {
166      console.error(this.TAG, "start error ! mInit false !");
167      this.mTextReaderCall?.onFail(TextReaderFail.UnInit);
168      return;
169    }
170    if (!infoArr) {
171      console.error(this.TAG, "start error ! infoArr null !");
172      this.mTextReaderCall?.onFail(TextReaderFail.TextReaderInfoNULL);
173      return;
174    }
175    // 朗读启动配置
176    const startParams: TextReader.StartParams = {
177      isMinibarHidden: this.mTextReaderInitData?.isMinibarNeeded ?? true,
178    }
179    // 填充朗读内容
180    let readInfoList: TextReader.ReadInfo[] = [];
181    for (let index = 0; index < infoArr.length; index++) {
182      const info = infoArr[index];
183      let tempInfo: TextReader.ReadInfo = {
184        id: " " + index,
185        title: {
186          text: info.title,
187          isClickable: true,
188        },
189        bodyInfo: info.content,
190        date: {
191          text: info.author ?? "",
192          isClickable: true,
193        },
194        author: {
195          text: info.author ?? "",
196          isClickable: true,
197        }
198      }
199      readInfoList.push(tempInfo);
200    }
201    this.mCurrentReadInfo = readInfoList[0];
202    // 启动朗读
203    await TextReader.start(readInfoList, this.mCurrentReadInfo.id, startParams);
204  }
205}
206

【HarmonyOS AI赋能】朗读控件详解》 是转载文章,点击查看原文


相关推荐


不止能聊,还能“动手”:谷歌AI代理掀起数字浪潮
墨风如雪2025/10/12

想象一下,你不再需要亲自盯着屏幕,点击鼠标,输入文字,一遍遍重复那些枯燥的在线操作。有一天,你只需对AI说一声:“帮我预订下周五的餐厅,找到离家最近的那个,并且确保能带宠物。”然后,它就能像一个训练有素的助手,熟练地在浏览器中穿梭,完成一系列复杂的任务——搜索、筛选、填写信息、预约…… 这并非遥远的科幻场景。就在最近,谷歌扔下了一颗重磅炸弹:Gemini 2.5 Computer Use模型正式登场。这不仅仅是一个新模型,更是谷歌向“计算机使用智能体”(CUA)这个未来战场吹响的号角,预示着AI


汽车软件开发的质量和安全管理流程
NewCarRen2025/10/10

摘要 软件开发流程是智能车辆(联网车辆和自动驾驶车辆)的核心,必须精心管理。自动化与联网功能的开发分别通过功能安全和网络安全开发流程实现,且需遵循相关标准,这些标准规定了流程、最佳实践、危害、威胁及管理策略。通过改进软件开发流程,智能车辆的人体工程学性能将得到提升。本文阐述了如何通过软件开发来管理实现自动化与联网功能的流程,以及是否可能改变管理团队的策略与软件开发流程。 1、引言 智能车辆是一种能够从周围环境中获取信息,并对信息进行处理,从而实现自主安全行驶且不造成任何伤害的车辆。此外,智


前端路由的秘密:手写一个迷你路由,看懂Hash和History的较量
良山有风来2025/10/9

你是不是也遇到过这样的场景?开发单页面应用时,页面跳转后刷新一下就404,或者URL里带着难看的#号,被产品经理吐槽不够优雅? 别担心,今天我就带你彻底搞懂前端路由的两种模式,手把手教你实现一个迷你路由,并告诉你什么场景该用哪种方案。 读完本文,你能获得一套完整的前端路由知识体系,从原理到实战,再到生产环境配置,一次性全搞定! 为什么需要前端路由? 想象一下,你正在开发一个后台管理系统。传统做法是每个页面都对应一个HTML文件,切换页面就要重新加载,体验特别差。 而前端路由让你可以在一个页面内实


Kubernetes核心技术与集群部署项目
企鹅侠客2025/10/7

从集群搭建到核心功能应用的完整流程,内容涵盖集群部署、核心组件、资源管理、安全机制、持久化、监控与应用交付等关键技术。首先介绍 Kubernetes 的架构与特性,深入讲解 kubeadm 与二进制两种集群搭建方式,包括 etcd 集群部署、Master 与 Node 组件安装、证书签发及高可用集群实现。核心技术部分系统解析 Pod 的运行机制、调度策略、健康检查与资源限制,讲解 Deployment、StatefulSet、DaemonSet、Job 等控制器的应用场景。配置管理方面介绍 Co


Nginx 配置负载均衡(详细版)
1加1等于2025/10/6

本文详细介绍关于Nginx 配置负载均衡,包括配置文件结构、多种负载均衡策略、如何修改均衡策略以及其他一些重要的配置。 本文目录 一、、Nginx 配置负载均衡1. 配置文件结构 二、Nginx 负载均衡策略1. 轮询(`默认策略`)2. 加权轮询3. IP 哈希4. 最少连接 三、修改负载均衡策略四、Nginx 负载均衡其他配置1. 健康检查2. 会话保持3. 超时设置 一、、Nginx 配置负载均衡 1. 配置文件结构 Nginx 的负载均衡配置主要


UNIX下C语言编程与实践16-UNIX 磁盘空间划分:引导块、超级块、i 节点区、数据区的功能解析
迎風吹頭髮2025/10/4

一、UNIX 磁盘空间划分的核心逻辑:为何分为四个区域? UNIX 文件系统在格式化时,会将磁盘分区(如 /dev/sda1)划分为引导块(Boot Block)、超级块(Super Block)、i 节点区(Inode Area)、数据区(Data Area)四个连续的区域。这种划分并非随意设计,而是为了实现“系统启动-文件系统管理-数据存储”的完整功能链路,确保磁盘空间的有序管理和高效访问。 核心定位:四个区域各司其职且相互依赖——引导块负责“启动系统”,超级块负责“管理文件系统全局信息


DeepSeek V3.1-Terminus、阿里 Qwen3-Max、ChatGPT Pulse 同周登场!| AI Weekly 9.22-9.28
AI信息Gap2025/10/3

卷,卷起来了! 📢 本周 AI 快讯 | 1 分钟速览🚀 1️⃣ 🚀 DeepSeek 发布 V3.1-Terminus :Agent 性能提升 28%,HLE 测试跃升全球第三,仅次于 Grok 4 和 GPT-5,SimpleQA 准确率达 96.8%。 2️⃣ 💰 阿里云栖大会七连发 :3800 亿 AI 基建投资起步,万亿参数 Qwen3-Max 对标 GPT-5,AIME 25 和 HMMT 数学测试满分 100 分。 3️⃣ 🖥️ Kimi 推出 OK Compu


[linux仓库]深入解析Linux动态链接与动态库加载:理解背后的原理与技巧
egoist20232025/10/2

🌟 各位看官好,我是egoist2023! 🌍 Linux == Linux is not Unix ! 🚀 今天来学习Linux的指令知识,并学会灵活使用这些指令。 👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享更多人哦! 目录 进程如何看到动态库 进程间如何共享库 动态链接 编译器对可执行程序动手脚 动态库相对地址  程序如何和库具体映射 程序怎么进行库调用 全局偏移量表GOT 库间依赖(看看即可) 总结 进程如何看到动态库


Excel转PDF不分页
Bella_chene2025/10/2

将Excel转成PDF后,会发现存在分页的现象,理想是希望将一整个表格按实际情况缩放显示到PDF的一页上去 操作办法:打开excel表格,ctrl+P打开打印页面,点击页面设置,选择“将工作表调整为一页” 然后在右侧就可以看到效果,点击保存就可以了


ptyhon 基础语法学习(对比php)
come1123410/2/2025

非常棒!我们已经系统地对比了 Python 和 PHP 的所有主要数据类型。分类Python 类型对应 PHP 概念关键点基础intfloatstrintfloatstringPython 字符串格式化用f-string序列listtuple索引数组list可变(用[]),tuple不可变(用()映射dict关联数组键值对集合 (用{}集合set的结果元素唯一且无序特殊boolNoneboolnullFalse和None(首字母大写)这份表格可以作为您未来的速查手册。

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0