HarmonyOS ArkWeb 系列之文本选中菜单定制:editMenuOptions 深度解析

文章目录

      • [和 onContextMenuShow 有什么区别?](#和 onContextMenuShow 有什么区别?)
      • [核心概念:TextMenuItem 和 TextMenuItemId](#核心概念:TextMenuItem 和 TextMenuItemId)
      • 文本菜单定制流程
      • 完整示例代码
      • [`textRange` 是什么](#textRange 是什么)
      • [系统内置 TextMenuItemId 一览](#系统内置 TextMenuItemId 一览)
      • 几个注意点
      • [和 `editMenuOptions` 对比:bindSelectionMenu](#和 editMenuOptions 对比:bindSelectionMenu)
      • 写在最后

选中网页里的文字,上面弹出一排小按钮(复制、剪切、粘贴......)------这排按钮叫"文本选中菜单"。editMenuOptions 让你完全控制这里出现什么。

和 onContextMenuShow 有什么区别?

别搞混:

  • onContextMenuShow:长按触发,适合图片、链接的右键菜单
  • editMenuOptions:选中文字后弹出的那一排操作按钮

两个是不同场景,API 也完全不同。

核心概念:TextMenuItem 和 TextMenuItemId

大白话解释:

TextMenuItem 就是菜单里的一个按钮,有三个属性:

  • content:显示的文字或图标资源
  • id:这个按钮的唯一标识,用来区分是哪个按钮被点了
  • icon(可选):按钮旁边的小图标

TextMenuItemId 是系统内置的 ID 枚举,用 TextMenuItemId.CUTTextMenuItemId.COPY 这样的方式引用系统默认按钮。自定义按钮用 TextMenuItemId.of('你的自定义ID') 创建。

文本菜单定制流程

完整示例代码

typescript 复制代码
import { webview } from '@kit.ArkWeb';

@Entry
@Component
struct WebTextMenuDemo {
  controller: webview.WebviewController = new webview.WebviewController();

  // onCreateMenu:决定菜单里放哪些按钮
  // 参数 menuItems 是系统默认的全部菜单项数组
  onCreateMenu(menuItems: Array<TextMenuItem>): Array<TextMenuItem> {
    // 第一步:从系统菜单里只保留我们想要的
    let items = menuItems.filter((menuItem) => {
      return (
        menuItem.id.equals(TextMenuItemId.CUT) ||
        menuItem.id.equals(TextMenuItemId.COPY) ||
        menuItem.id.equals(TextMenuItemId.PASTE)
      );
    });

    // 第二步:添加自定义菜单项
    let shareItem: TextMenuItem = {
      content: '分享',
      id: TextMenuItemId.of('action_share'),
      icon: $r('app.media.share_icon') // 替换为实际图标资源
    };

    let translateItem: TextMenuItem = {
      content: '翻译',
      id: TextMenuItemId.of('action_translate'),
      icon: $r('app.media.translate_icon')
    };

    // 在末尾追加自定义项
    items.push(shareItem);
    items.push(translateItem);

    return items;
  }

  // onMenuItemClick:某个菜单项被点击时的回调
  // 返回 true = 拦截(系统默认行为不执行)
  // 返回 false = 不拦截(系统默认行为照常执行)
  onMenuItemClick(menuItem: TextMenuItem, textRange: TextRange): boolean {
    if (menuItem.id.equals(TextMenuItemId.CUT)) {
      // 剪切:先做自己的逻辑,再拦截系统行为
      console.info('用户剪切了文字,范围:', textRange.start, '-', textRange.end);
      return false; // false = 系统剪切照常执行
    }

    if (menuItem.id.equals(TextMenuItemId.COPY)) {
      console.info('用户复制了文字');
      return false; // 不拦截,让系统正常复制
    }

    if (menuItem.id.equals(TextMenuItemId.of('action_share'))) {
      // 自定义分享逻辑
      console.info('用户点了分享');
      // ... 调用分享面板
      return true; // true = 菜单保持不关闭(适合需要二级操作的情况)
    }

    if (menuItem.id.equals(TextMenuItemId.of('action_translate'))) {
      console.info('用户点了翻译');
      // ... 调用翻译服务
      return true;
    }

    return false; // 默认不拦截
  }

  // 把两个回调封装成 EditMenuOptions 对象
  @State editMenuOptions: EditMenuOptions = {
    onCreateMenu: this.onCreateMenu,
    onMenuItemClick: this.onMenuItemClick
  };

  build() {
    Column() {
      Web({ src: $rawfile('index.html'), controller: this.controller })
        .editMenuOptions(this.editMenuOptions) // 挂载自定义菜单配置
        .width('100%')
        .height('100%')
    }
  }
}

textRange 是什么

onMenuItemClick 的第二个参数 textRange 包含用户选中的文字范围:

typescript 复制代码
interface TextRange {
  start?: number;  // 选中起始位置(字符索引)
  end?: number;    // 选中结束位置(字符索引)
}

你可以用这个范围去 JS 层获取对应的选中文本(配合 runJavaScript 注入脚本)。

系统内置 TextMenuItemId 一览

ID 说明
TextMenuItemId.CUT 剪切
TextMenuItemId.COPY 复制
TextMenuItemId.PASTE 粘贴
TextMenuItemId.SELECT_ALL 全选
TextMenuItemId.SHARE 分享
TextMenuItemId.TRANSLATE 翻译(部分系统支持)
TextMenuItemId.SEARCH 搜索

几个注意点

1. onCreateMenu 里修改数组顺序就是修改显示顺序

unshift 往前插、用 push 往后加,菜单按你返回的数组顺序排列。

2. 自定义项点击后 truefalse 的区别

对于自定义 ID 的按钮,true 表示点击后不关闭菜单 (适合多步操作),false 表示点击后菜单自动关闭

3. 系统 ID 点击后 truefalse 的区别

对于系统默认 ID(CUT/COPY 等),true 表示拦截系统行为 (你自己处理),false 表示让系统执行(正常复制粘贴)。

editMenuOptions 对比:bindSelectionMenu

特性 editMenuOptions bindSelectionMenu
适用对象 Web 内文字选中菜单 Web 内链接/图片长按菜单
菜单样式 跟随系统文字选中条 完全自定义 Popup/Sheet
支持预览 不支持 支持(PREVIEW_MENU
触发方式 选中文字时自动 长按指定元素类型

写在最后

editMenuOptions 是目前定制文字选中菜单最干净的方式,不需要拦截手势也不需要注入 JS。如果你的应用需要"选中文字后一键翻译/搜索/分享",这个 API 正好对味。

相关推荐
TrisighT1 天前
我用 AI 逆向了 ArkTS @Builder 的编译产物,看完再也不敢乱写嵌套了
ai编程·harmonyos·arkts
看谷秀3 天前
鸿蒙-part3-arkts下
arkts
TrisighT3 天前
ArkTS 的 @BuilderParam 你八成只用了皮毛——那个尾随闭包写法差点被我当 bug 删了
harmonyos·arkts·arkui
TrisighT4 天前
ArkTS 列表滚动时为什么会闪现旧数据?我扒了 LazyForEach 的复用逻辑
harmonyos·arkts·arkui
TrisighT5 天前
一个下午搞定 ArkTS 折叠面板?结果我从两点写到晚上九点
harmonyos·arkts·arkui
TrisighT12 天前
AI写埋点代码,35%覆盖率坑惨运营
harmonyos·arkts·arkui
TrisighT16 天前
AI写鸿蒙UI:10个跑崩8个,剩下2个看运气
ai编程·harmonyos·arkts
至乐活着16 天前
HarmonyOS开发深度解析:网络请求与数据持久化实战全攻略
网络请求·harmonyos·arkts·数据持久化·鸿蒙开发
ZJPRENO18 天前
2026华为HDC AI 编程核心成果总结
华为·arkts