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让系统菜单照常弹出。根据实际需求选择就行,不必过度封装。
