ReactNative项目OpenHarmony三方库集成实战:react-native-tts 语音播放
在移动应用开发中,文字转语音(Text-to-Speech,TTS)是一项非常实用的功能,广泛应用于语音播报、无障碍阅读、导航提示、语言学习等场景。是一个跨平台的 TTS 库,支持将文本转换为语音输出,让应用具备"开口说话"的能力,极大地提升了用户体验和应用的可访问性。库名称版本信息: 支持 RN 0.72 版本(已废弃)4.1.3: 支持 RN 0.72 版本4.2.0: 支持 RN 0.77
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
项目基于 RN 0.72.90 开发
测试过了可以播放
📋 前言
在移动应用开发中,文字转语音(Text-to-Speech,TTS)是一项非常实用的功能,广泛应用于语音播报、无障碍阅读、导航提示、语言学习等场景。react-native-tts 是一个跨平台的 TTS 库,支持将文本转换为语音输出,让应用具备"开口说话"的能力,极大地提升了用户体验和应用的可访问性。
🎯 库简介
基本信息
- 库名称:
react-native-tts - 版本信息:
4.1.1-0.0.3+@react-native-oh-tpl/react-native-tts: 支持 RN 0.72 版本(已废弃)4.1.3+@react-native-ohos/react-native-tts: 支持 RN 0.72 版本4.2.0+@react-native-ohos/react-native-tts: 支持 RN 0.77 版本
- 官方仓库: https://github.com/ak1394/react-native-tts
- 鸿蒙仓库: https://github.com/react-native-oh-library/react-native-tts
- 主要功能:
- 🔊 文字转语音
- ⏸️ 播放控制(暂停/恢复/停止)
- 🎛️ 语速和音调调节
- 🗣️ 多语音选择
- 📡 事件监听
为什么需要 TTS?
| 特性 | 应用场景 | 价值 |
|---|---|---|
| 语音播报 | 新闻、消息通知 | 解放双眼,提升效率 |
| 无障碍阅读 | 视障用户辅助 | 提升应用可访问性 |
| 导航提示 | 地图导航、驾驶辅助 | 安全便捷 |
| 语言学习 | 发音教学、听力练习 | 提升学习效果 |
| 智能客服 | 语音交互 | 提升用户体验 |
| 电子书阅读 | 有声读物 | 碎片化阅读 |
核心功能
| 功能 | 说明 | HarmonyOS 支持 |
|---|---|---|
| speak | 朗读文本 | ✅ |
| stop | 停止朗读 | ✅ |
| pause | 暂停朗读 | ✅ |
| resume | 恢复朗读 | ✅ |
| setDefaultRate | 设置语速 | ✅ |
| setDefaultPitch | 设置音调 | ✅ |
| voices | 获取可用语音列表 | ✅ |
| getInitStatus | 获取初始化状态 | ✅ |
| addEventListener | 添加事件监听 | ✅ |
| removeEventListener | 移除事件监听 | ✅ |
| setDucking | 降低其他音频音量 | ❌ |
| setDefaultEngine | 设置默认引擎 | ❌ |
| setDefaultVoice | 设置默认语音 | ❌ |
| setDefaultLanguage | 设置默认语言 | ❌ |
| engines | 获取可用引擎列表 | ❌ |
兼容性验证
在以下环境验证通过:
- RNOH: 0.72.90; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.2; ROM: 6.0.0
📦 安装步骤
1. 安装依赖
# RN 0.72 版本(本项目使用)
npm install @react-native-ohos/react-native-tts@4.1.3-rc.1
# RN 0.77 版本
npm install @react-native-ohos/react-native-tts@4.2.0-rc.1
# 或者使用 yarn
yarn add @react-native-ohos/react-native-tts
2. 验证安装
安装完成后,检查 package.json 文件:
{
"dependencies": {
"@react-native-ohos/react-native-tts": "^4.1.3-rc.1"
}
}
3. 类型定义配置(重要)
该库默认导出的是 react-native-tts,需要创建类型定义文件以获得完整的 TypeScript 类型支持。
文件路径: src/types/react-native-tts.d.ts
declare module "react-native-tts" {
interface Voice {
id: string;
name: string;
language: string;
quality?: number;
latency?: number;
networkRequired?: boolean;
notInstalled?: boolean;
}
interface SpeakOptions {
rate?: number;
pitch?: number;
language?: string;
voice?: string;
onWordBoundary?: boolean;
skipTransform?: boolean;
}
interface TtsStartEvent {
utteranceId: string;
}
interface TtsProgressEvent {
utteranceId: string;
location: number;
length: number;
}
interface TtsFinishEvent {
utteranceId: string;
}
interface TtsCancelEvent {
utteranceId: string;
}
interface TtsErrorEvent {
utteranceId: string;
error: string;
}
type TtsEventType =
| "tts-start"
| "tts-progress"
| "tts-finish"
| "tts-cancel"
| "tts-error";
interface TtsStatic {
speak(text: string, options?: SpeakOptions): Promise<string>;
stop(): void;
pause(): void;
resume(): void;
setDefaultRate(rate: number): void;
setDefaultPitch(pitch: number): void;
setDefaultLanguage(language: string): void;
setDefaultVoice(voiceId: string): void;
setDucking(enabled: boolean): void;
setIgnoreSilentSwitch(ignore: boolean): void;
voices(): Promise<Voice[]>;
engines(): Promise<any[]>;
getInitStatus(): Promise<void>;
requestInstallEngine(): Promise<void>;
requestInstallData(): Promise<void>;
addEventListener(
event: "tts-start",
callback: (event: TtsStartEvent) => void
): void;
addEventListener(
event: "tts-progress",
callback: (event: TtsProgressEvent) => void
): void;
addEventListener(
event: "tts-finish",
callback: (event: TtsFinishEvent) => void
): void;
addEventListener(
event: "tts-cancel",
callback: (event: TtsCancelEvent) => void
): void;
addEventListener(
event: "tts-error",
callback: (event: TtsErrorEvent) => void
): void;
removeEventListener(event: TtsEventType, callback: (event: any) => void): void;
}
const Tts: TtsStatic;
export default Tts;
}
在tpconfig.json中添加配置:
{
"include": [
"**/*.ts",
"**/*.tsx"
],
}
🔧 HarmonyOS 平台配置 ⭐
Link 配置
| 版本 | 是否支持 autolink | RN 框架版本 |
|---|---|---|
| ~4.2.0 | No | 0.77 |
| ~4.1.3 | Yes | 0.72 |
| <= 4.1.1-0.0.3@deprecated | No | 0.72 |
1. 在工程根目录的 oh-package.json5 添加 overrides 字段
打开 harmony/oh-package.json5,添加以下配置:
{
// ... 其他配置
"overrides": {
"@rnoh/react-native-openharmony": "0.72.90"
}
}
2. 引入原生端代码
打开 harmony/entry/oh-package.json5,添加以下依赖:
"dependencies": {
"@react-native-ohos/react-native-tts": "file:../../node_modules/@react-native-ohos/react-native-tts/harmony/rn_tts.har"
}
点击右上角的 sync 按钮,或者在终端执行:
cd entry
ohpm install
3. 配置 CMakeLists
打开 entry/src/main/cpp/CMakeLists.txt,添加:
project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
add_subdirectory("${RNOH_CPP_DIR}" ./rn)
# RNOH_BEGIN: manual_package_linking_1
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-tts/src/main/cpp" ./rn_tts)
# RNOH_END: manual_package_linking_1
add_library(rnoh_app SHARED
"./PackageProvider.cpp"
"${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)
# RNOH_BEGIN: manual_package_linking_2
+ target_link_libraries(rnoh_app PUBLIC rnoh_tts)
# RNOH_END: manual_package_linking_2
4. 引入 RNTTSPackage
打开 entry/src/main/cpp/PackageProvider.cpp,添加:
#include "RNOH/PackageProvider.h"
+ #include "RNTTSPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
+ std::make_shared<RNTTSPackage>(ctx)
};
}
5. 在 ArkTS 侧引入 RNTTSPackage
打开 entry/src/main/ets/RNPackagesFactory.ts,添加:
import { RNTTSPackage } from '@react-native-ohos/react-native-tts/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
return [
new RNTTSPackage(ctx)
];
}
📖 API 详解
speak - 朗读文本
将文本转换为语音并朗读出来。
类型:(text: string, options?: SpeakOptions) => Promise<string>
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| text | string | 是 | 要朗读的文本内容 |
| options | SpeakOptions | 否 | 朗读选项配置 |
SpeakOptions 配置:
| 参数 | 类型 | 说明 |
|---|---|---|
| rate | number | 语速(0.5 - 2.0) |
| pitch | number | 音调(0.5 - 2.0) |
| language | string | 语言代码(如 ‘zh-CN’) |
| voice | string | 语音 ID |
| onWordBoundary | boolean | 是否触发单词边界事件 |
| skipTransform | boolean | 是否跳过文本转换 |
返回值:Promise<string> - 返回朗读任务的 ID
使用场景:
- 新闻播报
- 消息通知
- 导航提示
- 电子书阅读
import React, { useState, useCallback } from "react";
import { View, Text, StyleSheet, TouchableOpacity, Alert } from "react-native";
import Tts from "react-native-tts";
const SpeakExample = () => {
const [isSpeaking, setIsSpeaking] = useState(false);
const speakText = useCallback(async () => {
try {
setIsSpeaking(true);
const utteranceId = await Tts.speak(
"欢迎使用文字转语音功能,这是一个简单的示例。",
{
rate: 1.0,
pitch: 1.0,
}
);
console.log("朗读任务 ID:", utteranceId);
} catch (error) {
Alert.alert("错误", "朗读失败");
setIsSpeaking(false);
}
}, []);
return (
<View style={styles.container}>
<Text style={styles.title}>文字转语音示例</Text>
<TouchableOpacity
style={[styles.button, isSpeaking && styles.buttonDisabled]}
onPress={speakText}
disabled={isSpeaking}
>
<Text style={styles.buttonText}>
{isSpeaking ? "正在朗读..." : "开始朗读"}
</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 20,
alignItems: "center",
},
title: {
fontSize: 18,
fontWeight: "600",
marginBottom: 20,
},
button: {
backgroundColor: "#007AFF",
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
buttonDisabled: {
backgroundColor: "#CCCCCC",
},
buttonText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "500",
},
});
export default SpeakExample;
stop - 停止朗读
停止当前的语音朗读。
类型:() => void
import Tts from "react-native-tts";
const stopSpeaking = () => {
Tts.stop();
console.log("已停止朗读");
};
pause - 暂停朗读
暂停当前的语音朗读。
类型:() => void
import Tts from "react-native-tts";
const pauseSpeaking = () => {
Tts.pause();
console.log("已暂停朗读");
};
resume - 恢复朗读
恢复暂停的语音朗读。
类型:() => void
import Tts from "react-native-tts";
const resumeSpeaking = () => {
Tts.resume();
console.log("已恢复朗读");
};
setDefaultRate - 设置默认语速
设置全局默认的语音朗读速度。
类型:(rate: number) => void
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| rate | number | 是 | 语速值,范围 0.5(最慢)- 2.0(最快),默认 1.0 |
import React, { useState } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import Tts from "react-native-tts";
const RateExample = () => {
const [currentRate, setCurrentRate] = useState(1.0);
const setRate = (rate: number) => {
Tts.setDefaultRate(rate);
setCurrentRate(rate);
};
const speakTest = () => {
Tts.speak("这是语速测试文本");
};
return (
<View style={styles.container}>
<Text style={styles.label}>当前语速: {currentRate.toFixed(1)}</Text>
<View style={styles.buttonGroup}>
{[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map((rate) => (
<TouchableOpacity
key={rate}
style={[
styles.rateButton,
currentRate === rate && styles.rateButtonActive,
]}
onPress={() => setRate(rate)}
>
<Text style={styles.rateButtonText}>{rate}x</Text>
</TouchableOpacity>
))}
</View>
<TouchableOpacity style={styles.testButton} onPress={speakTest}>
<Text style={styles.testButtonText}>测试语速</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 20,
},
label: {
fontSize: 16,
marginBottom: 12,
},
buttonGroup: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
marginBottom: 20,
},
rateButton: {
paddingHorizontal: 16,
paddingVertical: 8,
backgroundColor: "#E5E5EA",
borderRadius: 8,
},
rateButtonActive: {
backgroundColor: "#007AFF",
},
rateButtonText: {
color: "#333333",
fontWeight: "500",
},
testButton: {
backgroundColor: "#34C759",
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
alignItems: "center",
},
testButtonText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "500",
},
});
export default RateExample;
setDefaultPitch - 设置默认音调
设置全局默认的语音音调。
类型:(pitch: number) => void
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| pitch | number | 是 | 音调值,范围 0.5(低沉)- 2.0(尖锐),默认 1.0 |
import React, { useState } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import Tts from "react-native-tts";
const PitchExample = () => {
const [currentPitch, setCurrentPitch] = useState(1.0);
const setPitch = (pitch: number) => {
Tts.setDefaultPitch(pitch);
setCurrentPitch(pitch);
};
const speakTest = () => {
Tts.speak("这是音调测试文本");
};
return (
<View style={styles.container}>
<Text style={styles.label}>当前音调: {currentPitch.toFixed(1)}</Text>
<View style={styles.buttonGroup}>
{[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map((pitch) => (
<TouchableOpacity
key={pitch}
style={[
styles.pitchButton,
currentPitch === pitch && styles.pitchButtonActive,
]}
onPress={() => setPitch(pitch)}
>
<Text style={styles.pitchButtonText}>{pitch}</Text>
</TouchableOpacity>
))}
</View>
<TouchableOpacity style={styles.testButton} onPress={speakTest}>
<Text style={styles.testButtonText}>测试音调</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 20,
},
label: {
fontSize: 16,
marginBottom: 12,
},
buttonGroup: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
marginBottom: 20,
},
pitchButton: {
paddingHorizontal: 16,
paddingVertical: 8,
backgroundColor: "#E5E5EA",
borderRadius: 8,
},
pitchButtonActive: {
backgroundColor: "#FF9500",
},
pitchButtonText: {
color: "#333333",
fontWeight: "500",
},
testButton: {
backgroundColor: "#34C759",
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
alignItems: "center",
},
testButtonText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "500",
},
});
export default PitchExample;
voices - 获取可用语音列表
获取设备上可用的语音列表。
类型:() => Promise<Voice[]>
返回值:Promise<Voice[]> - 语音列表
Voice 对象结构:
| 属性 | 类型 | 说明 |
|---|---|---|
| id | string | 语音 ID |
| name | string | 语音名称 |
| language | string | 语言代码 |
| quality | number | 语音质量 |
| latency | number | 延迟 |
| networkRequired | boolean | 是否需要网络 |
| notInstalled | boolean | 是否未安装 |
import React, { useState, useCallback } from "react";
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from "react-native";
import Tts from "react-native-tts";
type Voice = {
id: string;
name: string;
language: string;
};
const VoicesExample = () => {
const [voices, setVoices] = useState<Voice[]>([]);
const [loading, setLoading] = useState(false);
const loadVoices = useCallback(async () => {
setLoading(true);
try {
const voiceList = await Tts.voices();
setVoices(voiceList as Voice[]);
} catch (error) {
console.error("获取语音列表失败:", error);
} finally {
setLoading(false);
}
}, []);
const renderVoiceItem = ({ item }: { item: Voice }) => (
<View style={styles.voiceItem}>
<Text style={styles.voiceName}>{item.name}</Text>
<Text style={styles.voiceLanguage}>语言: {item.language}</Text>
<Text style={styles.voiceId}>ID: {item.id}</Text>
</View>
);
return (
<View style={styles.container}>
<TouchableOpacity style={styles.loadButton} onPress={loadVoices}>
<Text style={styles.loadButtonText}>
{loading ? "加载中..." : "获取语音列表"}
</Text>
</TouchableOpacity>
<FlatList
data={voices}
keyExtractor={(item) => item.id}
renderItem={renderVoiceItem}
ListEmptyComponent={
<Text style={styles.emptyText}>暂无语音数据</Text>
}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
loadButton: {
backgroundColor: "#007AFF",
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
alignItems: "center",
marginBottom: 16,
},
loadButtonText: {
color: "#FFFFFF",
fontSize: 16,
fontWeight: "500",
},
voiceItem: {
backgroundColor: "#F5F5F5",
padding: 12,
borderRadius: 8,
marginBottom: 8,
},
voiceName: {
fontSize: 16,
fontWeight: "600",
color: "#333333",
},
voiceLanguage: {
fontSize: 14,
color: "#666666",
marginTop: 4,
},
voiceId: {
fontSize: 12,
color: "#999999",
marginTop: 4,
},
emptyText: {
textAlign: "center",
color: "#999999",
marginTop: 40,
},
});
export default VoicesExample;
getInitStatus - 获取初始化状态
获取 TTS 引擎的初始化状态。
类型:() => Promise<void>
返回值:Promise<void> - 初始化完成时 resolve
import Tts from "react-native-tts";
const checkInitStatus = async () => {
try {
await Tts.getInitStatus();
console.log("TTS 引擎已初始化");
return true;
} catch (error) {
console.error("TTS 引擎未初始化:", error);
return false;
}
};
addEventListener - 添加事件监听
添加 TTS 事件监听器。
类型:(event: TTSEvent, callback: (data: any) => void) => void
事件类型:
| 事件名称 | 说明 | 回调数据 |
|---|---|---|
| tts-start | 开始朗读 | { utteranceId: string } |
| tts-progress | 朗读进度 | { utteranceId: string, location: number } |
| tts-finish | 朗读完成 | { utteranceId: string } |
| tts-cancel | 朗读取消 | { utteranceId: string } |
| tts-error | 朗读错误 | { utteranceId: string, error: string } |
import React, { useEffect, useState } from "react";
import { View, Text, StyleSheet } from "react-native";
import Tts from "react-native-tts";
const EventListenerExample = () => {
const [status, setStatus] = useState("等待中...");
useEffect(() => {
Tts.addEventListener("tts-start", (event) => {
console.log("开始朗读:", event.utteranceId);
setStatus("正在朗读...");
});
Tts.addEventListener("tts-progress", (event) => {
console.log("朗读进度:", event.location);
setStatus(`朗读进度: ${event.location}`);
});
Tts.addEventListener("tts-finish", (event) => {
console.log("朗读完成:", event.utteranceId);
setStatus("朗读完成");
});
Tts.addEventListener("tts-cancel", (event) => {
console.log("朗读取消:", event.utteranceId);
setStatus("朗读取消");
});
Tts.addEventListener("tts-error", (event) => {
console.log("朗读错误:", event.error);
setStatus(`朗读错误: ${event.error}`);
});
return () => {
Tts.removeEventListener("tts-start", () => {});
Tts.removeEventListener("tts-progress", () => {});
Tts.removeEventListener("tts-finish", () => {});
Tts.removeEventListener("tts-cancel", () => {});
Tts.removeEventListener("tts-error", () => {});
};
}, []);
return (
<View style={styles.container}>
<Text style={styles.statusText}>状态: {status}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 20,
},
statusText: {
fontSize: 16,
color: "#333333",
},
});
export default EventListenerExample;
removeEventListener - 移除事件监听
移除 TTS 事件监听器。
类型:(event: TTSEvent, callback: (data: any) => void) => void
import Tts from "react-native-tts";
const handleTtsStart = (event: any) => {
console.log("开始朗读:", event);
};
// 添加监听
Tts.addEventListener("tts-start", handleTtsStart);
// 移除监听
Tts.removeEventListener("tts-start", handleTtsStart);
📋 完整示例
import React, { useState, useEffect, useCallback } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
TextInput,
SafeAreaView,
StatusBar,
Alert,
} from "react-native";
import Tts from "react-native-tts";
type Voice = {
id: string;
name: string;
language: string;
};
type TTSStatus = "idle" | "speaking" | "paused" | "error";
const App: React.FC = () => {
const [text, setText] = useState(
"欢迎使用文字转语音功能。这是一个完整的示例应用,展示了 TTS 的各种功能。"
);
const [status, setStatus] = useState<TTSStatus>("idle");
const [rate, setRate] = useState(1.0);
const [pitch, setPitch] = useState(1.0);
const [voices, setVoices] = useState<Voice[]>([]);
const [currentUtteranceId, setCurrentUtteranceId] = useState<string>("");
useEffect(() => {
initTTS();
return () => {
Tts.stop();
};
}, []);
const initTTS = async () => {
try {
await Tts.getInitStatus();
console.log("TTS 初始化成功");
Tts.addEventListener("tts-start", (event) => {
setStatus("speaking");
setCurrentUtteranceId(event.utteranceId || "");
});
Tts.addEventListener("tts-finish", () => {
setStatus("idle");
setCurrentUtteranceId("");
});
Tts.addEventListener("tts-cancel", () => {
setStatus("idle");
setCurrentUtteranceId("");
});
Tts.addEventListener("tts-error", (event) => {
setStatus("error");
console.error("TTS 错误:", event.error);
});
const voiceList = await Tts.voices();
setVoices(voiceList as Voice[]);
} catch (error) {
console.error("TTS 初始化失败:", error);
Alert.alert("错误", "TTS 初始化失败");
}
};
const speak = useCallback(async () => {
if (!text.trim()) {
Alert.alert("提示", "请输入要朗读的文本");
return;
}
try {
await Tts.speak(text);
} catch (error) {
Alert.alert("错误", "朗读失败");
}
}, [text]);
const stop = useCallback(() => {
Tts.stop();
setStatus("idle");
}, []);
const pause = useCallback(() => {
Tts.pause();
setStatus("paused");
}, []);
const resume = useCallback(() => {
Tts.resume();
setStatus("speaking");
}, []);
const handleRateChange = useCallback((newRate: number) => {
setRate(newRate);
Tts.setDefaultRate(newRate);
}, []);
const handlePitchChange = useCallback((newPitch: number) => {
setPitch(newPitch);
Tts.setDefaultPitch(newPitch);
}, []);
const getStatusText = () => {
switch (status) {
case "speaking":
return "正在朗读...";
case "paused":
return "已暂停";
case "error":
return "发生错误";
default:
return "就绪";
}
};
const getStatusColor = () => {
switch (status) {
case "speaking":
return "#34C759";
case "paused":
return "#FF9500";
case "error":
return "#FF3B30";
default:
return "#007AFF";
}
};
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
<View style={styles.header}>
<Text style={styles.headerTitle}>文字转语音</Text>
<View style={[styles.statusBadge, { backgroundColor: getStatusColor() }]}>
<Text style={styles.statusText}>{getStatusText()}</Text>
</View>
</View>
<ScrollView style={styles.scrollView}>
<View style={styles.section}>
<Text style={styles.sectionTitle}>输入文本</Text>
<TextInput
style={styles.textInput}
value={text}
onChangeText={setText}
placeholder="请输入要朗读的文本"
multiline
numberOfLines={4}
textAlignVertical="top"
/>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>播放控制</Text>
<View style={styles.controlButtons}>
<TouchableOpacity
style={[
styles.controlButton,
status === "speaking" && styles.buttonDisabled,
]}
onPress={speak}
disabled={status === "speaking"}
>
<Text style={styles.controlButtonText}>朗读</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.controlButton,
styles.pauseButton,
status !== "speaking" && styles.buttonDisabled,
]}
onPress={pause}
disabled={status !== "speaking"}
>
<Text style={styles.controlButtonText}>暂停</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.controlButton,
styles.resumeButton,
status !== "paused" && styles.buttonDisabled,
]}
onPress={resume}
disabled={status !== "paused"}
>
<Text style={styles.controlButtonText}>恢复</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.controlButton,
styles.stopButton,
status === "idle" && styles.buttonDisabled,
]}
onPress={stop}
disabled={status === "idle"}
>
<Text style={styles.controlButtonText}>停止</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>语速调节</Text>
<Text style={styles.currentValue}>当前语速: {rate.toFixed(2)}x</Text>
<View style={styles.sliderContainer}>
{[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0].map((value) => (
<TouchableOpacity
key={value}
style={[
styles.sliderButton,
rate === value && styles.sliderButtonActive,
]}
onPress={() => handleRateChange(value)}
>
<Text
style={[
styles.sliderButtonText,
rate === value && styles.sliderButtonTextActive,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>音调调节</Text>
<Text style={styles.currentValue}>当前音调: {pitch.toFixed(2)}</Text>
<View style={styles.sliderContainer}>
{[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0].map((value) => (
<TouchableOpacity
key={value}
style={[
styles.sliderButton,
pitch === value && styles.sliderButtonActive,
]}
onPress={() => handlePitchChange(value)}
>
<Text
style={[
styles.sliderButtonText,
pitch === value && styles.sliderButtonTextActive,
]}
>
{value}
</Text>
</TouchableOpacity>
))}
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>可用语音 ({voices.length})</Text>
{voices.length === 0 ? (
<Text style={styles.emptyText}>暂无可用语音</Text>
) : (
voices.slice(0, 5).map((voice) => (
<View key={voice.id} style={styles.voiceItem}>
<Text style={styles.voiceName}>{voice.name}</Text>
<Text style={styles.voiceLanguage}>语言: {voice.language}</Text>
</View>
))
)}
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#F5F5F5",
},
header: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
padding: 16,
backgroundColor: "#FFFFFF",
borderBottomWidth: 1,
borderBottomColor: "#E5E5EA",
},
headerTitle: {
fontSize: 20,
fontWeight: "700",
color: "#333333",
},
statusBadge: {
paddingHorizontal: 12,
paddingVertical: 4,
borderRadius: 12,
},
statusText: {
color: "#FFFFFF",
fontSize: 12,
fontWeight: "600",
},
scrollView: {
flex: 1,
},
section: {
backgroundColor: "#FFFFFF",
margin: 16,
marginBottom: 0,
marginTop: 16,
padding: 16,
borderRadius: 12,
},
sectionTitle: {
fontSize: 16,
fontWeight: "600",
color: "#333333",
marginBottom: 12,
},
textInput: {
backgroundColor: "#F5F5F5",
borderRadius: 8,
padding: 12,
fontSize: 16,
minHeight: 100,
color: "#333333",
},
controlButtons: {
flexDirection: "row",
gap: 8,
},
controlButton: {
flex: 1,
backgroundColor: "#007AFF",
paddingVertical: 12,
borderRadius: 8,
alignItems: "center",
},
pauseButton: {
backgroundColor: "#FF9500",
},
resumeButton: {
backgroundColor: "#34C759",
},
stopButton: {
backgroundColor: "#FF3B30",
},
buttonDisabled: {
opacity: 0.5,
},
controlButtonText: {
color: "#FFFFFF",
fontSize: 14,
fontWeight: "600",
},
currentValue: {
fontSize: 14,
color: "#666666",
marginBottom: 12,
},
sliderContainer: {
flexDirection: "row",
flexWrap: "wrap",
gap: 8,
},
sliderButton: {
paddingHorizontal: 14,
paddingVertical: 8,
backgroundColor: "#E5E5EA",
borderRadius: 8,
},
sliderButtonActive: {
backgroundColor: "#007AFF",
},
sliderButtonText: {
fontSize: 14,
color: "#333333",
fontWeight: "500",
},
sliderButtonTextActive: {
color: "#FFFFFF",
},
emptyText: {
fontSize: 14,
color: "#999999",
textAlign: "center",
paddingVertical: 20,
},
voiceItem: {
backgroundColor: "#F5F5F5",
padding: 12,
borderRadius: 8,
marginBottom: 8,
},
voiceName: {
fontSize: 14,
fontWeight: "600",
color: "#333333",
},
voiceLanguage: {
fontSize: 12,
color: "#666666",
marginTop: 4,
},
});
export default App;
⚠️ 注意事项
遗留问题
| 问题 | 说明 | Issue |
|---|---|---|
| 三方引擎相关功能 | 无法实现三方引擎功能 | issue#2 |
| 语言类型限制 | 目前只支持中文 | issue#3 |
| setDucking | 无法实现降低其他音频音量 | issue#4 |
| setIgnoreSilentSwitch | 无法实现忽略静音开关 | issue#5 |
| setDefaultVoice | 无法设置默认语音 | issue#6 |
| 边合成边播放 | 边合成边播放功能问题 | issue#7 |
| 参数无效 | skipTransform、onWordBoundary 参数无效 | issue#8 |
使用建议
- 初始化检查:使用前建议调用
getInitStatus()检查 TTS 引擎是否已初始化 - 事件监听:建议添加事件监听器以获取朗读状态
- 资源释放:组件卸载时调用
stop()停止朗读 - 语速音调:根据实际场景调整合适的语速和音调
常见问题
Q: 朗读没有声音?
A: 检查设备音量设置,确保媒体音量已开启。同时确认 TTS 引擎已正确初始化。
Q: 只支持中文吗?
A: 目前 HarmonyOS 版本存在语言类型限制,主要支持中文。其他语言支持正在完善中。
Q: 如何切换不同的语音?
A: 使用 voices() 获取可用语音列表,但目前 setDefaultVoice 功能暂不支持。
📚 参考资料
- 官方文档: https://github.com/ak1394/react-native-tts
- 鸿蒙适配仓库: https://github.com/react-native-oh-library/react-native-tts
- 问题反馈: https://github.com/react-native-oh-library/react-native-tts/issues
更多推荐




所有评论(0)