文章目录普通菜单 vs 预览菜单预览链接目标页面预览图片大图重点预览 Web 必须设 hitTestBehavior预览尺寸从哪来PREVIEW_MENU vs 普通菜单 对比写在最后你肯定见过微信或 Safari 里的这种效果长按一个链接先弹出一个小窗口预览链接目标页同时显示打开/复制/分享等操作。这就是预览菜单PREVIEW_MENU。ArkWeb 也支持这个而且还支持预览图片。普通菜单 vs 预览菜单普通的bindSelectionMenu只能弹出一个操作列表没有预览区域。PREVIEW_MENU在操作列表上方或旁边增加了一块预览区域这个预览区域完全可以自定义——放一个 Web 组件预览链接目标或者放一张图片大图预览都行。预览链接目标页面长按链接 → 小窗加载链接目标 → 操作菜单打开/复制。import{webview}fromkit.ArkWeb;import{pasteboard}fromkit.BasicServicesKit;interfacePreviewParam{url:Resource|string|undefined;width:number;height:number;}EntryComponentstruct PreviewMenuLinkDemo{controller:webview.WebviewControllernewwebview.WebviewController();privateresult:WebContextMenuResult|undefinedundefined;StatelinkURL:string;StateprogressValue:number0;StateprogressVisible:booleanfalse;uiContext:UIContextthis.getUIContext();// 预览区域嵌入一个迷你 Web 组件加载目标链接BuilderPreviewBuilder($$:PreviewParam){Column(){// 顶部进度条仅加载时显示Progress({value:this.progressValue,total:100,type:ProgressType.Linear}).style({strokeWidth:3,enableSmoothEffect:true}).opacity(this.progressVisible?1:0).backgroundColor(Color.White).width(100%)// 用一个独立 WebviewController 渲染预览不影响主 WebWeb({src:$$.url,controller:newwebview.WebviewController()}).javaScriptAccess(true).fileAccess(true).onlineImageAccess(true).domStorageAccess(true).onPageBegin((){this.progressValue0;this.progressVisibletrue;}).onProgressChange((event){this.progressValueevent.newProgress;}).onPageEnd((){this.progressVisiblefalse;}).hitTestBehavior(HitTestMode.None)// 预览窗不响应手势}.width($$.width).height($$.height)}// 链接操作菜单BuilderLinkMenuBuilder(){Menu(){MenuItem({content:复制链接}).onClick((){constdatapasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN,this.linkURL);pasteboard.getSystemPasteboard().setData(data);})MenuItem({content:打开链接}).onClick((){this.controller.loadUrl(this.linkURL);})}}build(){Column(){Web({src:$rawfile(index.html),controller:this.controller}).javaScriptAccess(true).fileAccess(true).onlineImageAccess(true).imageAccess(true).domStorageAccess(true).bindSelectionMenu(WebElementType.LINK,this.LinkMenuBuilder,WebResponseType.LONG_PRESS,{onDisappear:(){this.result?.closeContextMenu();},// 关键传入预览 Builderpreview:this.PreviewBuilder({url:this.linkURL,width:500,height:400}),menuType:MenuType.PREVIEW_MENU,// 指定为预览菜单类型}).onContextMenuShow((event){if(event){this.resultevent.result;this.linkURLevent.param.getLinkUrl();returntrue;}returnfalse;}).zoomAccess(true)}}onBackPress():boolean|void{try{if(this.controller.accessStep(-1)){this.controller.backward();returntrue;}}catch(error){console.error(onBackPress failed);}returnfalse;}}预览图片大图长按图片 → 显示图片预览 复制操作。import{webview}fromkit.ArkWeb;interfaceImagePreviewParam{previewImage:Resource|string|undefined;width:number;height:number;}// 全局 Builder方便复用也可以放在组件内BuilderfunctionImagePreviewBuilder($$:ImagePreviewParam){Column(){Image($$.previewImage).objectFit(ImageFit.Fill).autoResize(true)}.width($$.width).height($$.height)}EntryComponentstruct PreviewMenuImageDemo{controller:webview.WebviewControllernewwebview.WebviewController();privateresult:WebContextMenuResult|undefinedundefined;StatepreviewImage:Resource|string|undefinedundefined;StatepreviewWidth:number300;StatepreviewHeight:number200;uiContext:UIContextthis.getUIContext();BuilderImageMenuBuilder(){Menu(){MenuItem({content:复制图片}).onClick((){this.result?.copyImage();this.result?.closeContextMenu();})}}build(){Column(){Web({src:$rawfile(index.html),controller:this.controller}).javaScriptAccess(true).imageAccess(true).onlineImageAccess(true).bindSelectionMenu(WebElementType.IMAGE,this.ImageMenuBuilder,WebResponseType.LONG_PRESS,{onDisappear:(){this.result?.closeContextMenu();},// 图片预览区用全局 Builder 传入当前图片preview:ImagePreviewBuilder({previewImage:this.previewImage,width:this.previewWidth,height:this.previewHeight}),menuType:MenuType.PREVIEW_MENU,}).onContextMenuShow((event){if(event){this.resultevent.result;// 获取图片预览尺寸px 转 vpthis.previewWidththis.uiContext.px2vp(event.param.getPreviewWidth());this.previewHeightthis.uiContext.px2vp(event.param.getPreviewHeight());// 处理本地 rawfile 路径constsrcevent.param.getSourceUrl();if(src.startsWith(resource://rawfile/)){this.previewImage$rawfile(src.substring(19));}else{this.previewImagesrc;}returntrue;}returnfalse;})}}}重点预览 Web 必须设 hitTestBehavior预览窗里的 Web 组件如果不加这个属性用户滑动预览区域会导致手势冲突菜单无法正常操作.hitTestBehavior(HitTestMode.None)// 预览 Web 不消费手势事件预览尺寸从哪来图片预览的宽高建议用event.param.getPreviewWidth()和getPreviewHeight()获取这两个方法返回的是图片在网页上渲染的实际像素尺寸再用px2vp转换这样预览大小和原图比例一致。PREVIEW_MENU vs 普通菜单 对比特性普通菜单PREVIEW_MENU预览区域无有自定义 Builder适用场景快速操作链接预览、图片大图动画效果系统默认系统默认带预览展开动画代码复杂度低中写在最后预览菜单这个功能在内容类 App 里特别有用比如新闻 App 里长按链接可以先预览一眼再决定要不要打开体验比直接跳走要好很多。预览 Web 记得设hitTestBehavior(HitTestMode.None)这个细节很容易忘。