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 正好对味。

相关推荐
2301_7681034918 小时前
HarmonyOS趣味相机实战第29篇:AudioRenderer合成快门声、并发门闩与资源释放
harmonyos·arkts·资源管理·音频开发·audiorenderer
熊猫钓鱼>_>21 小时前
ArkUI 动画实战:从理论到交互,构建流畅的鸿蒙动效体验
华为·架构·交互·harmonyos·arkts·arkui·native
2301_768103491 天前
HarmonyOS趣味相机实战第31篇:图片文档List、详情弹层与删除状态闭环
list·harmonyos·arkts·状态管理·arkui
2301_768103492 天前
HarmonyOS趣味相机实战第24篇:前摄镜像、旋转补偿与识别框坐标统一
图像处理·harmonyos·arkts·camerakit·坐标映射
AD02273 天前
HarmonyOS应用实战-启示散页-05-随机抽答案要避免连续重复:把算法放进服务层
harmonyos·arkts·鸿蒙开发
AD02273 天前
HarmonyOS应用实战-启示散页-06-仪式感动画不要卡业务:用 DrawingPage 隔离抽取和转场
harmonyos·arkts·鸿蒙开发
AD02273 天前
HarmonyOS应用实战-启示散页-08-提问历史别无限长:用去重、截断和 Sheet 入口做轻量记忆
harmonyos·arkts·鸿蒙开发
咏方舟【长江支流】3 天前
【编程】(1)-跨平台跨语言C#、Java、鸿蒙ArkTS三端通用实体接口
java·c#·ai编程·harmonyos·arkts·咏方舟-长江支流·用宝框架
2301_768103494 天前
HarmonyOS趣味相机实战第19篇:CameraKit输出Profile协商、宽高比评分与会话提交
harmonyos·arkts·camerakit·photosession·设备适配
熊猫钓鱼>_>4 天前
ArkTS 方舟编程语言 · 原创快速入门教程
运维·架构·ts·harmonyos·arkts·鸿蒙·js