HarmonyOS 7 ComposeTitleBarV2 升级踩坑:外层 onClick 不生效,右侧图标又读不出来

把老的 ComposeTitleBar 换成 ComposeTitleBarV2,看起来只是名称多了一个 V2,实际却涉及两件容易漏掉的事:标题栏本身不适合直接挂通用事件;只有图标的右侧操作项,也不应只凭图形让用户猜用途。外层点击事件的限制不是 V2 才出现的回归:旧版参考也写明不支持通用事件。V2 文档进一步解释了 __Common__ 节点为什么可能让事件表现出乎预期。

这篇讨论 API 26.0.0 新增的 ComposeTitleBarV2,并把迁移时容易误判的旧版限制和新版差异分开。下面两个案例可以分别放在独立页面检查;图标资源名采用官方示例中使用的系统资源。本文代码按官方 API 签名写出,当前本地没有 API 26.0.0 SDK 和对应设备,因此不能宣称已经完成 DevEco 编译或真机验收。文末列出上机时要核对的结果。

两个问题的排查路径

先弄清组件边界

官方参考把 ComposeTitleBarV2 标为 API 26.0.0 起提供,使用 @ComponentV2 和状态管理 V2;支持手机、鸿蒙电脑、平板和电视,但不包括穿戴设备。它的右侧菜单项要用 ComposeTitleBarV2MenuItem 类实例,而不是把旧组件的普通对象数组原样复制过来。

更值得注意的是,官方明确提醒:在 ComposeTitleBarV2 上设置通用属性或通用事件时,编译工具链可能额外生成 __Common__ 节点,属性或事件并非直接作用到标题栏本身,结果可能与预期不一致。因此,右侧菜单按钮的点击,应放在对应菜单项的 action 里;需要给整个区域加布局和背景时,应由外围容器承担,而不是赌组件本体对通用属性的处理。

迁移时还有一个真正改变了默认行为的细节:旧版 ComposeTitleBarMenuItem.isEnabled 默认 false,新版 ComposeTitleBarV2MenuItem.isEnabled 默认 true。如果原代码省略该字段,升级后右侧按钮可能突然可点。先逐项显式填写 isEnabled,不要把“外层事件失效”与“菜单默认启用”混成同一个问题。

案例一:把整个标题栏当按钮,点击为什么不可靠

下面是一个容易误写的思路:给标题栏整体加 .onClick(...),希望点击右侧保存图标时执行保存。这里即使某个预览环境似乎有反应,也不能把它当成菜单项行为契约;官方对通用事件有明确限制。

// 不建议把菜单操作绑定在 ComposeTitleBarV2 整体上。
ComposeTitleBarV2({ title: '编辑记录', menuItems: this.menuItems })
  .onClick(() => this.saveRecord())

改成菜单项自己负责动作。下面用可见的计数代替真实存储,专门验证点击路由;点击图标计数加一,点击标题或空白处计数不变。实际接入保存服务时,仍需处理提交中禁用和重复点击。

import { ComposeTitleBarV2, ComposeTitleBarV2MenuItem } from '@kit.ArkUI';

@Entry
@ComponentV2
struct EditPage {
  @Local actionCount: number = 0;
  @Local menuItems: Array<ComposeTitleBarV2MenuItem> = [
    new ComposeTitleBarV2MenuItem({
      value: $r('sys.media.ohos_save_button_filled'),
      label: '保存',
      accessibilityText: '保存当前记录',
      accessibilityDescription: '保存修改并留在当前页面',
      accessibilityLevel: 'yes',
      isEnabled: true,
      action: () => this.saveRecord()
    })
  ];

  private saveRecord(): void {
    this.actionCount += 1;
  }

  build(): void {
    Column() {
      ComposeTitleBarV2({
        title: '编辑记录',
        subtitle: '本地草稿',
        menuItems: this.menuItems
      })
      Text(`保存入口触发次数:${this.actionCount}`).padding(16)
    }
  }
}

这个案例要检查两个点:点保存图标时,只有该菜单项的 action 被调用;点击标题文字或空白处,不会误触。真正接入异步保存后,还要把提交中状态与 isEnabled 的刷新一起验证,别只测一次正常点击。

操作错误绑定的观察目标改为 action 后的观察目标
点标题或空白处外层 .onClick 不可当成可靠的菜单契约计数保持 0
点右侧保存外层回调可能不触发或命中范围不符合预期计数从 0 变 1
再点一次保存不能仅凭第一次点击判断重复提交安全示例计数变 2;实际保存服务须另设提交中保护

这张表是待设备核对的预期,不是已经在 API 26 设备上观察到的结果。若 action 没触发,先检查该菜单项的 isEnabled、图标资源和实际点击区域;若点击标题也触发了保存,检查是否还有外围容器的事件处理。

案例二:图标看得见,屏幕朗读却不知道它做什么

右侧菜单按钮没有可见文字时,如果既不设置 label 也不设置 accessibilityText,官方给出的默认朗读文本可能只是空白。accessibilityText 是操作名称,accessibilityDescription 用来补充后果,accessibilityLevel 决定该元素能否被辅助服务识别。这三个字段不是装饰性文案。

import { ComposeTitleBarV2, ComposeTitleBarV2MenuItem } from '@kit.ArkUI';

@Entry
@ComponentV2
struct ReviewPage {
  @Local confirmationOpened: number = 0;
  @Local menuItems: Array<ComposeTitleBarV2MenuItem> = [
    new ComposeTitleBarV2MenuItem({
      value: $r('sys.media.ohos_ic_public_remove'),
      label: '删除',
      isEnabled: true,
      accessibilityText: '删除当前记录',
      accessibilityDescription: '将先弹出确认框,不会立即删除',
      accessibilityLevel: 'yes',
      action: () => this.openDeleteConfirmation()
    })
  ];

  private openDeleteConfirmation(): void {
    // 只记录进入确认流程的次数;这里没有删除数据。
    this.confirmationOpened += 1;
  }

  build(): void {
    Column() {
      ComposeTitleBarV2({ title: '记录详情', menuItems: this.menuItems })
      Text(`进入确认流程:${this.confirmationOpened} 次`).padding(16)
    }
  }
}

这段代码不以“按钮可点击”作为验收终点。上机时打开屏幕朗读,焦点移到删除按钮,应该听到清楚的动作名称和后果;再双击按钮,页面只更新进入确认流程的次数,没有执行删除。真正接入对话框时,还应确认取消后数据原样保留。把 accessibilityLevel 改成 'no' 做对照,按钮就不该成为朗读焦点。它和“保存”案例分别覆盖了通用事件边界和可访问性信息,两种问题不能互相替代。

朗读测试要做 A/B 对照:先去掉 label、accessibilityText,记录焦点读出的内容;再恢复这两个字段及 accessibilityDescription,记录动作与后果是否完整。官方说明 accessibilityText 未设置时可由 label 提供默认文本;两者都没有时默认是空白。不要写成“没配 accessibilityText 就一定不会朗读”。辅助服务的焦点顺序、实际措辞仍以设备测试为准。

升级时该怎么选

如果项目还在 API 26.0.0 之前,不要仅为了名称带 V2 就直接替换组件。先核对目标 SDK、设备类型和状态管理方案;旧版 ComposeTitleBar 的对象结构与新版 ComposeTitleBarV2MenuItem 类不要混用。真正迁移时,建议逐个菜单项列出 value、label、isEnabled、action 和可访问性说明,再验证禁用、重复点击、屏幕朗读、横竖屏与鸿蒙电脑窗口尺寸变化。

排查顺序可以固定为:先看 menuItems 是否真的是 ComposeTitleBarV2MenuItem 实例;再看 isEnabled(V1/V2 默认值相反);接着只在菜单项 action 内记录一次可见计数;最后再做屏幕朗读对照。这样一轮检查就能把“没触发”“不该触发却触发了”和“触发了但读不出来”拆开。

还有一条细节:新版菜单项可以设置 symbolStyle,官方说明它比 value 优先。若升级后图标和设计稿不一致,先看是不是同时配了二者,而不是反复换 value 图片。左侧头像用 item 字段,但官方注明它不支持触发菜单项的 action 和 isEnabled 行为,不要拿它当右侧操作按钮使用。

验证记录与边界

可在 API 26.0.0 DevEco 工程里建两个独立页面,各自粘贴对应示例后执行:编译、运行、点击区域检查、屏幕朗读焦点与文案检查,再换鸿蒙电脑窗口宽度检查标题栏布局。当前环境未安装 API 26.0.0 SDK,以上是待完成的设备验收清单,不是已通过的测试结果。代码只演示官方组件用法与问题定位;保存服务和删除弹窗需由项目实现并另行测试。

参考:华为开发者文档《ComposeTitleBarV2》,更新于 2026-09-08: https://developer.huawei.com/consumer/en/doc/harmonyos-references/ohos-arkui-advanced-composetitlebarv2 。旧版对照《ComposeTitleBar》: https://developer.huawei.com/consumer/en/doc/harmonyos-references-V13/ohos-arkui-advanced-composetitlebar-V13 。

Logo

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

更多推荐