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

相关推荐
贾伟康3 小时前
【时光清单|14】HarmonyOS ArkTS 主导航实战:统一页面入口、返回路径和参数校验
harmonyos·arkts·arkui·navigation·路由管理
贾伟康8 小时前
【时光清单|05】HarmonyOS ArkTS 倒计时卡片实战:适配 2x2、2x4 与 4x4 多尺寸
harmonyos·arkts·arkui·服务卡片·formextensionability
贾伟康11 小时前
【时光清单|20】HarmonyOS ArkTS AppGallery 发布复查实战:核对包名、版本、设备、素材和离线声明
harmonyos·arkts·agc·appgallery·发布复查
贾伟康14 小时前
【时光清单|12】HarmonyOS ArkTS 单页复杂状态实战:拆分 Index 中的导航、弹层和编辑草稿
harmonyos·arkts·状态管理·arkui·navigation
贾伟康17 小时前
【时光清单|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
harmonyos·arkts·arkui·深色模式·设计系统
贾伟康17 小时前
【时光清单|15】HarmonyOS ArkTS 本地状态持久化实战:让保存、删除和页面返回后的数据即时一致
harmonyos·arkts·状态管理·preferences·arkdata
黑臂麒麟2 天前
HarmonyOS鸿蒙实战应用7:随手账本——数据导出与分享
华为·app·arkts·鸿蒙
贾伟康2 天前
【天体运行模拟|11】HarmonyOS ArkTS 收藏与笔记实战:同步知识页和个人学习记录
harmonyos·arkts·preferences·多设备适配·arkdata
黑臂麒麟2 天前
屏幕信息全解析:HarmonyOS的分辨率、刷新率、折叠状态一个API搞定
华为·arkts·鸿蒙
黑臂麒麟2 天前
HarmonyOS 网络连接诊断实战:检测网络状态、WiFi 切换、弱网监测一网打尽
网络·华为·arkts·鸿蒙