三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

HarmonyOS ArkWeb 系列之 右键菜单完全自定义:onContextMenuShow 用法详解

HarmonyOS ArkWeb 系列之 右键菜单完全自定义:onContextMenuShow 用法详解

文章目录

      • 先搞清楚"右键菜单"是什么
      • 流程图:右键菜单触发全流程
      • 最简实现:把系统菜单换成自己的
      • 几个容易踩的坑
      • `event.param` 都能拿到什么
      • `result` 能执行哪些操作
      • 写在最后

长按网页里的链接或图片,系统弹出一个菜单——这个菜单能不能换成我们自己设计的样式?答案是可以。onContextMenuShow就是干这个的。

先搞清楚"右键菜单"是什么

在手机上,长按网页中的文字、图片、链接,会弹出一个操作菜单(复制、粘贴、保存图片……)。在桌面端就是鼠标右键菜单。

ArkWeb 的onContextMenuShow回调在这个时机触发,给你两个关键参数:

  • event.param:当前长按位置的信息(坐标、链接地址、图片地址……)
  • event.result:操作句柄,调它的方法可以执行复制/粘贴/关闭菜单等系统行为

返回true表示"我自己处理,系统别弹了";返回false表示"用系统默认菜单就行"。

流程图:右键菜单触发全流程

最简实现:把系统菜单换成自己的

import{webview}from'@kit.ArkWeb';import{pasteboard}from'@kit.BasicServicesKit';@Entry@Componentstruct WebContextMenuDemo{controller:webview.WebviewController=newwebview.WebviewController();// 保存菜单操作句柄,点菜单项时调用privateresult:WebContextMenuResult|undefined=undefined;@StatelinkUrl:string='';@StateshowMenu:boolean=false;@StateoffsetY:number=0;uiContext:UIContext=this.getUIContext();// 用 @Builder 声明自定义菜单内容@BuilderMenuBuilder(){Menu(){MenuItem({content:'复制图片'}).width(200).height(50).onClick(()=>{this.result?.copyImage();// 调系统复制图片this.showMenu=false;})MenuItem({content:'剪切'}).width(200).height(50).onClick(()=>{this.result?.cut();this.showMenu=false;})MenuItem({content:'复制'}).width(200).height(50).onClick(()=>{this.result?.copy();this.showMenu=false;})MenuItem({content:'粘贴'}).width(200).height(50).onClick(()=>{this.result?.paste();this.showMenu=false;})MenuItem({content:'复制链接'}).width(200).height(50).onClick(()=>{// 手动写入剪贴板letpasteData=pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN,this.linkUrl);pasteboard.getSystemPasteboard().setData(pasteData,(error)=>{if(error){return;}});this.showMenu=false;})MenuItem({content:'全选'}).width(200).height(50).onClick(()=>{this.result?.selectAll();this.showMenu=false;})}.width(150).height(300)}build(){Column(){Web({src:$rawfile('index.html'),controller:this.controller}).onContextMenuShow((event)=>{if(event){// 保存句柄和链接地址this.result=event.result;this.linkUrl=event.param.getLinkUrl();// 根据长按 Y 坐标定位菜单弹出位置this.offsetY=Math.max(this.uiContext.px2vp(event.param.y()??0)-0,0);}this.showMenu=true;returntrue;// 关键:返回 true 阻止系统菜单}).bindPopup(this.showMenu,{builder:this.MenuBuilder(),enableArrow:false,placement:Placement.LeftTop,offset:{x:0,y:this.offsetY},mask:false,onStateChange:(e)=>{if(!e.isVisible){this.showMenu=false;this.result?.closeContextMenu();// 菜单消失时通知内核关闭}}})}}}

几个容易踩的坑

坑1:忘记调closeContextMenu()

菜单消失后如果不调这个,Web 内核状态不同步,下次再长按可能出问题。一定要在onStateChange里菜单不可见时调一次。

坑2:event.param.y()返回的是像素值

屏幕坐标是 px,ArkUI 布局用的是 vp,需要用this.getUIContext().px2vp()转换,否则菜单位置会乱。

坑3:result保存时机

result必须在onContextMenuShow回调里保存,不能用成员变量直接操作,因为每次长按都是一个新的result对象。

event.param都能拿到什么

方法说明
x()/y()长按位置坐标(px)
getLinkUrl()链接地址
getUnfilteredLinkUrl()原始链接(未过滤)
getSourceUrl()图片/媒体资源地址
existsImageContents()当前位置是否有图片
getPreviewWidth()/getPreviewHeight()图片预览尺寸(px)

result能执行哪些操作

方法说明
copy()复制选中内容
cut()剪切选中内容
paste()粘贴
copyImage()复制图片
selectAll()全选
closeContextMenu()关闭上下文菜单

写在最后

onContextMenuShow的设计很灵活,你既可以完全替换菜单,也可以只监听事件做一些统计、日志记录,然后返回false让系统菜单照常弹出。根据实际需求选择就行,不必过度封装。

← 返回列表