字幕页上调整字号或颜色,改变的是显示样式;麦克风 PCM 写入改变的是音频输入状态。两条状态如果混成一句“字幕已更新”,用户既不知道视觉配置是否生效,也不知道当前有没有音频输入。前端页面应让它们并列而不互相推导。

在这里插入图片描述

当前页面将样式配置与音频输入状态放在同一可见区域,但不把它们合并成一个结果判断。
在这里插入图片描述

样式参数只负责组件外观

@State private captionFontSize: AICaptionFontSize = AICaptionFontSize.NORMAL;
@State private captionFontColor: string = '#FFFFFFFF';

private buildOptions(): AICaptionOptions {
  return {
    sourceLanguage: this.sourceLanguage,
    targetLanguage: this.targetLanguage,
    fontSize: this.captionFontSize,
    fontColor: this.captionFontColor
  };
}

这些字段能够说明当前组件请求使用何种字号与颜色;不能说明页面已经产生了新的字幕文本。

PCM 输入单独拥有运行状态

private readonly audioDataCallback = (buffer: ArrayBuffer): void => {
  try {
    this.captionController.writeAudio({ data: new Uint8Array(buffer) });
    this.pcmWriteCount += 1;
    this.lastAudioWriteAt = timestamp();
    this.audioInputState = '正在从真实麦克风写入 PCM';
  } catch (error) {
    this.audioInputState = 'PCM 写入失败';
    this.audioErrorMessage = formatRuntimeError(error as Error);
  }
};

选择字号 / 颜色

字幕样式状态

AICaptionOptions

麦克风 PCM 回调

writeAudio

写入次数与输入状态

组件显示层

运行状态区

页面可以同时显示样式值、组件准备状态、PCM 写入次数和错误信息,但不能因为其中任一项正常就把另一项标记为成功。

视觉调整与声音输入,是两条不同的页面状态线

字幕页的“变大一点”“换成暖黄”属于阅读辅助;PCM 写入属于能力输入。它们都可能发生在用户观看字幕的同一时刻,却不应该共用“字幕已更新”这种笼统状态。前者回答的是“我现在能否看清”;后者回答的是“页面是否正在向组件交付音频”。如果一个状态覆盖另一个,用户既无法知道样式是否被保存,也无法判断输入链路为什么没有继续。

状态线 页面字段 操作入口 能说明什么 不能说明什么
样式线 captionFontSizecaptionFontColor 字号、颜色按钮 当前组件选项请求的外观 字幕已经产生
生命周期线 componentState、回调时间 组件就绪与错误回显 组件准备或异常状态 麦克风正在采集
输入线 audioInputState、写入次数 启动或停止声音输入 页面是否尝试写入 PCM 字幕语义正确
阅读线 当前轨道的字体效果 片段与轨道视图 页面视觉样式如何呈现 文本来源与质量

通过这四条状态线,用户能在界面上形成正确的阅读顺序:先确认当前样式,再看组件是否准备,然后判断声音输入有没有开始,最后才观察输出区域。这样即使某一步出错,也不会把“黄色字体已选中”误读成“语音内容已经识别”,更不会把“PCM 正在写入”理解成颜色一定已经应用。

点击字号或颜色

更新页面样式状态

buildOptions 读取字号与颜色

AICaptionComponent 使用当前视觉配置

组件准备完成

允许启动声音输入

申请麦克风权限

采集 PCM

writeAudio

输入状态与写入次数

视觉呈现

运行状态呈现

图中的两条分支会在组件处相遇,但没有任何一条可以替另一条做结论。字号和颜色只作为 options 的一部分;PCM 回调只说明页面收到并尝试转交音频缓冲区。页面应让用户同时看到它们,而不是为了简洁把所有细节折叠成一个“运行正常”的徽标。
在这里插入图片描述

样式按钮应只改样式状态

字号选择通常来自一组直观的阅读档位。界面可以把 18sp22sp26sp 直接呈现为按钮,同时在内部将它们映射到组件认可的字体枚举。这个映射属于前端适配:用户使用熟悉的可读尺寸,组件获得明确的配置值。重要的是,点击动作不应触碰 PCM 写入次数、麦克风状态或人工复核状态。

private setFontSize(size: number): void {
  this.fontSize = size;
  if (size === 22) {
    this.captionFontSize = AICaptionFontSize.BIG;
  } else if (size === 26) {
    this.captionFontSize = AICaptionFontSize.LARGE;
  } else {
    this.captionFontSize = AICaptionFontSize.NORMAL;
  }
  this.localFeedback = `本地参考字号已调整为 ${size}sp;真实组件字号选项同步更新。`;
}

这段代码能够说明:用户选择的阅读尺寸同步映射为组件的字体枚举,且页面反馈停在“选项同步更新”。它不能说明组件已重新准备,也不能说明现有字幕文本已经按新字号重新输出。若界面要展示即时视觉差异,可以更新本地阅读层;若要判断组件端实际呈现,则仍应看组件的生命周期与真实运行观察。

颜色切换也应保持同样的边界。页面可以提供白色、暖黄、青蓝等有语义的可读选项,并在按钮上使用边框或背景表达选中态。不要把“选中黄字”写成“高亮字幕已生效”,因为颜色值只是配置;当组件未准备、输入未启动或没有输出时,所谓“生效”缺少具体对象。

private setCaptionFontColor(fontColor: string): void {
  this.captionFontColor = fontColor;
  this.localFeedback =
    `本地参考字幕颜色已切换为${this.captionColorLabel()};真实组件颜色选项同步更新。`;
}

这段代码能够说明:当前颜色状态被集中写入,并且页面保留了人类可读的颜色名称。它不能证明任何输入音频已经被转写,也不能证明颜色在所有系统字体、亮度和背景下都拥有同样的可读性。前端可以把颜色作为当前配置展示,但最终可读性仍需要结合真实界面环境观察。

buildOptions() 让样式进入组件,但不接管输入状态

当样式状态更新后,组件配置需要从同一个地方读取当前字号与颜色。集中构造 options 的意义在于防止某个按钮只改变页面预览、另一个按钮才改变组件参数。与此同时,这个构造函数不应顺手把 PCM 状态写进去,因为输入状态来自真实采集回调,两者的数据来源不同。

private buildOptions(): AICaptionOptions {
  return {
    sourceLanguage: this.sourceLanguage,
    targetLanguage: this.targetLanguage,
    fontSize: this.captionFontSize,
    fontColor: this.captionFontColor,
    onPrepared: () => { /* 更新组件就绪状态 */ },
    onError: (error: BusinessError) => { /* 更新错误状态 */ }
  };
}

这段代码能够说明:语言与样式配置在创建组件选项时一并读取,准备和错误结果则回到独立的运行状态区。它不能说明组件会在每次点选后立即产生新的文本,也不能说明外观参数等同于声音输入参数。用户因此可以先选择“更大、更亮”的阅读条件,再从另一区域确认是否具备输入条件,而不是被一个混合状态误导。

PCM 写入必须以真实回调为准

声音输入不是一个按钮文案,而是一串会变化的运行事实:组件先要准备;用户需要完成麦克风授权;采集器启动后才可能收到缓冲区;每次缓冲区到来才会尝试向控制器写入。任何一步失败,页面都应该让输入状态停在真实位置,而不是继续显示“正在识别”。

private readonly audioDataCallback = (buffer: ArrayBuffer): void => {
  try {
    this.captionController.writeAudio({ data: new Uint8Array(buffer) });
    this.pcmWriteCount += 1;
    this.lastAudioWriteAt = timestamp();
    this.audioInputState = '正在从真实麦克风写入 PCM';
  } catch (error) {
    this.audioInputState = 'PCM 写入失败';
    this.audioErrorMessage = formatRuntimeError(error as Error);
  }
};

这段代码能够说明:只有收到一段音频缓冲区并成功交给控制器后,页面才递增写入次数并显示“正在写入 PCM”;异常则被记录为输入错误。它不能证明组件已经将这段缓冲区转化为字幕,也不能证明字幕内容完整或翻译正确。写入次数是输入链路的可观察证据,不是质量指标。

输入状态 页面真正知道什么 不应扩写成什么
等待组件准备 尚未满足启动采集的前置条件 语言不支持
麦克风未授权 用户尚未授予或拒绝权限 字幕能力失败
真实麦克风采集已启动 采集器已经启动 每一段音频都已写入
正在写入 PCM 至少一次写入回调成功 已得到完整字幕
PCM 写入失败 当前写入抛出错误 页面样式设置无效

按钮位置要服务于状态判断

在前端布局上,字号和颜色属于低风险、可反复尝试的阅读控制,适合放在字幕轨道上方、靠近用户的阅读视线。声音输入则影响隐私与设备资源,应该放在运行状态卡附近,并明确展示授权和采集状态。不要把“启动声音输入”夹在字号按钮中间;那样会把一项需要用户慎重判断的权限动作伪装成普通外观调整。

推荐的视觉层级是:第一行语言与视图;第二行字号与颜色;第三行组件状态、麦克风授权与 PCM 写入;第四行才是字幕轨道或参考内容。宽屏可以让控制区左右并列,紧凑屏幕则按这个顺序纵向排布。按钮宽度可以随内容自适应,但状态文字应始终有稳定位置,不要在每次切换颜色时跳动布局。

区域 最适合放置的元素 为什么
阅读控制 字号、颜色、轨道视图 操作后可立即理解视觉影响
运行状态 组件状态、最近回调、错误摘要 解释为什么输入还不能开始
输入控制 授权、启动、停止、写入次数 聚焦隐私和资源行为
内容区域 当前轨道与来源提示 不承担配置或授权决策

在这里插入图片描述

三条操作路径,检查页面有没有把状态混在一起

路径一:先调大字号,再启动输入

选择大字号后,页面应立即显示新的选中态和当前字号;PCM 写入次数保持原值。之后启动输入,若组件尚未准备,应只在输入区提示等待,不要撤销字号选择或把字号状态改为失败。

路径二:输入正在进行时切换颜色

颜色按钮可以更新当前颜色配置与本地阅读参考;输入区仍继续显示已有的写入次数和最近写入时间。页面不得因为颜色变更而把输入状态重置为“未启动”,除非用户明确执行了停止或重新创建输入会话的动作。

路径三:PCM 写入失败后调整样式

输入区应保留错误摘要与失败状态,样式区仍允许用户调节字号、颜色。两个区域各自可用,用户不会因一个输入异常而失去阅读控制,也不会因样式按钮仍可点击而误以为输入问题已解决。

在同一屏上呈现四种反馈,避免用户只盯着字幕区域

字幕页面很容易把大面积的轨道内容做得很醒目,状态信息则缩成角落的一行小字。这样一来,用户一看到已有文本,就自然把它当成当前运行结果;当输入停住或样式没有按预期变化时,又找不到应看的状态。更稳妥的界面结构,是让阅读区保持主视觉,同时在它的上方或旁侧固定四个短反馈:语言方向、当前字号与颜色、组件状态、PCM 输入状态。

四个反馈都不需要占用很大空间,但每一个必须有完整的文字而不是只用颜色:例如“中文 → 英文”“26sp / 暖黄”“组件已准备”“等待 PCM 输入”。用户只需扫一眼就能知道,当前看到的到底是配置、生命周期还是输入活动。若其中一个状态不确定,应该用“等待”或“失败”明确写出,不用空白区域让用户自行猜测。

反馈卡 推荐主文本 推荐辅助文本 颜色的辅助作用
阅读配置 26sp · 暖黄 当前阅读层配置 强调已选中,不承载结果结论
组件状态 已准备 / 等待 / 失败 最近回调时间或错误摘要 区分生命周期分支
输入状态 正在写入 / 未启动 / 已停止 写入次数与最近时间 指示是否有活动输入
内容来源 本地参考 / 当前观察 需要人工确认的边界 防止静态内容被误读

这样的结构也便于紧凑布局。宽屏可以把四项排成一条摘要带;窄屏则以两列或纵向短卡展示。无论屏幕多小,应该优先保留状态文本,而不是为了腾出轨道高度把“等待组件准备”“麦克风未授权”隐藏掉。真正影响用户下一步动作的信息,比展示更多固定文本更有价值。

从音频格式读取参数,不让页面硬编码输入条件

启动声音输入时,采集器需要匹配组件所需的音频信息。页面不应凭经验把采样率、声道数和位深写成固定常量,因为这些数值属于当前组件会话的输入约束。由控制器取得音频描述并用于创建采集器,既减少了配置分叉,也能让错误状态更贴近真实问题。

const audioInfo = this.captionController.getAudioInfo();
this.audioCapturer = await audio.createAudioCapturer({
  streamInfo: {
    samplingRate: audioInfo.sampleRate,
    channels: audioInfo.soundChannel,
    sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
    encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
  },
  capturerInfo: {
    source: audio.SourceType.SOURCE_TYPE_MIC,
    capturerFlags: 0
  }
});

这段代码能够说明:输入采集器使用控制器当前给出的采样率和声道信息创建,并明确为原始音频数据配置采集来源。它不能说明设备一定支持这一组参数,也不能说明创建成功后马上就有 PCM 回调。页面应把“采集启动失败”作为独立错误状态,并保留当前样式选择,避免用户误以为调整字体能修复输入参数问题。

对界面工程师而言,这里还有一个重要的可见边界:音频参数的摘要可以在输入状态卡里显示为“当前采集参数”,但不需要把底层对象和完整错误栈倾倒给用户。用户需要知道的是系统是否允许开始、页面是否已启动采集、最后一次写入在哪里;开发排查所需的完整细节可以保留在受控日志中,而不是变成公开正文或界面主文案。

停止输入应与样式重置彻底分开

许多页面在“重置”按钮中同时做三件事:恢复字号、停止采集、清空错误。这种一键清理看似省事,却会损失用户最需要的上下文——为什么刚才没有输入、最后一次写入是什么时候、当前样式是否真的恢复。更可靠的做法是为输入生命周期提供明确的停止与释放逻辑,而将样式重置留给阅读控制。

private async stopAudioInput(): Promise<void> {
  const capturer = this.audioCapturer;
  this.audioCapturer = undefined;
  if (capturer === undefined) {
    return;
  }
  try {
    capturer.off('readData', this.audioDataCallback);
    await capturer.stop();
    await capturer.release();
    this.audioInputState = '真实麦克风采集已停止并释放';
  } catch (error) {
    this.audioInputState = '麦克风资源释放失败';
    this.audioErrorMessage = formatRuntimeError(error as Error);
  }
}

这段代码能够说明:页面在停止采集时取消回调、停止采集器并释放资源,同时将结果写入输入状态。它不能证明操作系统已清除所有音频相关缓存,也不能影响用户刚才选择的字号和颜色。把资源释放与阅读配置隔离,用户就能在停止后继续查看当前样式,或在下次启动前按自己的节奏调整视觉条件。

可读性不是把颜色调亮,而是给状态留出对比关系

字幕的颜色选择往往服务于复杂背景下的阅读,但“黄色看起来更醒目”不应成为唯一设计依据。页面应同时考虑文本与背景的对比、按钮选中态是否能被非颜色线索辨认、错误状态是否与普通提示区分,以及大字号下会不会挤压相邻状态。这样调整样式时,用户看到的不只是颜色变化,还能稳定读出按钮、状态卡和轨道标题的层次。

一个实用检查方式是:分别选择三种颜色和三档字号,确认选中态既有文字也有边框或背景差异;确认组件等待、输入失败等状态不会被字幕颜色覆盖;确认紧凑屏幕下按钮换行后仍能完整看到“颜色”“字号”和当前值。这个检查聚焦前端可见性,并不对字幕识别效果作任何推断。

在这里插入图片描述

常见界面误导

颜色按钮变色后显示“字幕已更新”

颜色变色只说明当前页面选择发生改变,最多说明组件 options 将读取这个颜色。没有运行输入和输出证据时,不应写成字幕结果已经更新。

写入次数大于零就显示“识别正常”

写入次数只能证明输入回调至少成功过。识别状态、输出内容、术语准确性都属于另一层观察,页面需要留出独立位置。

将麦克风授权视为组件准备完成

授权解决的是设备输入许可;组件准备来自组件生命周期。二者任一缺失,输入都不应被写成“进行中”。

为了简洁而只留下一个状态徽标

一个徽标很难同时表达样式、组件、权限、采集和输出。拆成短而明确的状态行,比一个泛化的“正常”更能减少误操作。

FAQ

选择 26sp 后,为什么不应立即显示“字幕已生成”?

字号是视觉配置,不会产生声音输入或字幕结果。页面只应说明当前字号选项已经更新。

PCM 写入次数增加,是否代表当前颜色已经被组件采用?

不代表。写入次数来自音频回调;颜色来自组件配置。二者可以同时存在,但没有互相证明关系。

为什么要在页面上显示最后一次 PCM 写入时间?

它帮助用户区分“采集器曾启动过”和“当前仍有输入活动”,也便于排查输入停在何时;它不评价字幕内容。

麦克风被拒绝时,样式按钮是否应该禁用?

不需要。阅读样式与麦克风授权是不同操作;保留样式控制可以让用户继续调整页面,同时清晰显示输入无法启动的原因。

组件已准备但写入次数为零,页面该怎么表达?

可显示“组件已准备,等待 PCM 输入”。这比“已识别”或“失败”更符合当前可观察事实。

是否可以把本地参考字幕当作颜色效果的唯一依据?

可以用它检查页面阅读层的颜色与字号,但不能据此推导真实组件输出或输入链路。两类结论应分别表达。

停止输入后为什么还保留最后一次写入时间?

最后写入时间是会话上下文,能说明输入何时停止活动;它不等于输入仍在继续。页面应同时显示“已停止”的当前状态。

附录:HarmonyOS 6.1.1 新特性开发环境与真机验证准入

1. 版本硬基线

本批新特性统一以 HarmonyOS 6.1.1 API 24 为目标版本。项目 sourceproject/build-profile.json5 必须保持:

{
  "compatibleSdkVersion": "6.1.1(24)",
  "targetSdkVersion": "6.1.1(24)",
  "runtimeOS": "HarmonyOS"
}

开发者不得为了绕过构建错误,把项目静默改为 API 26 或其他版本。版本变化会同时改变 API 声明、兼容设备、文章结论和文章事实范围。

2. 编译环境准入

在 DevEco Studio 的 SDK Manager 中,必须选择与项目一致的 HarmonyOS 6.1.1(API 24) SDK。仅有 system-image 只能启动模拟器,不能证明 ArkTS 项目可以编译。至少应核对以下编译组件:

在这里插入图片描述

组件 作用 准入要求
hms/ets ArkTS/ETS API 声明与编译 目录存在,元数据与 Hvigor 兼容
hms/native Native 编译支持 目录存在,元数据与 Hvigor 兼容
hms/toolchains 编译、签名和设备工具链 目录存在,hdc 可执行
hms/previewer 预览与设计期支持 目录存在,版本与 SDK 对齐
openharmony/toolchains 设备安装、启动与调试 hdc.exe 可调用

硬性判定不是“SDK Manager 显示了 API 24”,而是构建已经越过 SDK 扫描并进入 CompileArkTS。本项目曾遇到组件 metaVersion: 3.1.0 与项目自带 Hvigor 扫描器不兼容,最终报 00303168 SDK component missing;此时不能进入特性 API 编码和文章结论阶段。

3. 推荐构建链路

当前已验证可用的是 DevEco Studio 内置 Hvigor 与 DevEco JBR,而不是项目自带的旧/不兼容 Hvigor 组合:

$env:DEVECO_SDK_HOME='D:\Program Files\Huawei\DevEco Studio\sdk'
$env:JAVA_HOME='D:\Program Files\Huawei\DevEco Studio\jbr'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
& 'D:\Program Files\Huawei\DevEco Studio\tools\hvigor\bin\hvigorw.bat' `
  --no-daemon --mode module -p module=entry@default -p product=default assembleHap --stacktrace

准入日志必须至少出现:

Finished :entry:default@CompileArkTS
Finished :entry:default@PackageHap
BUILD SUCCESSFUL

如果失败停在 SDK 扫描、依赖解析或 ArkTS 编译之前,结论只能写“环境未解锁”。不要根据 IDE 能打开项目、预览器能显示页面或旧 HAP 仍能安装,推导新特性 API 可用。

4. HAP 安装与启动环境

安装验证至少记录设备、包名、HAP 来源和结果。当前项目基线如下:

项目 要求/已验证值
包名 com.csdn.harmonyos.featuredemos
项目 API compatibleSdkVersion=6.1.1(24)targetSdkVersion=6.1.1(24)
设备 API 与项目兼容范围匹配,当前 API 24
releaseType 项目、SDK、设备保持一致,当前为 Release
设备形态 本批 Demo 以横向 Pad 为主要截图形态;手机需单独复核
HAP 来源 当前 SDK 重新构建的产物,不沿用旧 HAP
$hdc='D:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\toolchains\hdc.exe'
& $hdc install -r 'sourceproject\entry\build\default\outputs\default\entry-default-unsigned.hap'
& $hdc shell aa start -a EntryAbility -b com.csdn.harmonyos.featuredemos

install bundle successfully 只证明 HAP 与设备的安装条件匹配;start ability successfully 只证明应用可以启动。两者均不证明 Map、Camera、Notification 听觉、AI 字幕或通行证识别已经成功。
在这里插入图片描述
在这里插入图片描述

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐