在HarmonyOS 6购物比价或电商类应用中AI推荐模块可生成富媒体商品卡片图文混排、价格标签、评分星级用户希望一键分享给好友——自动滚动截取完整推荐卡片生成长图预览后可保存相册或直接调系统分享。常见坑是直接用componentSnapshot.get()只截到可视区、长图拼接重复、Web图文详情未开全页绘制、WRITE_MEDIA权限动态申请被拒。本文将参考官方行业实践与51CTO社区快照分享案例用componentSnapshot.get()Scroller分段滚屏 PixelMap.crop/writePixelsSync拼接 photoAccessHelperSaveButton落盘 完整实现AI推荐卡片的长截图分享功能。一、需求拆解与页面结构1. 典型AI推荐卡片结构Column (AI推荐区包在 Scroll 内) ├── 推荐标题 为你推荐 ├── 商品图文块1 (Image 名称 ¥价格 ★评分) ├── 商品图文块2 ├── 商品图文块3 └── 底部说明 由HarmonyOS AI生成截图目标 上述Scroll内全部内容不含底部加入购物车操作栏。2. 交互流程用户点分享推荐 → 禁交互 → 滚回顶部 → 循环【滚一段截一屏crop新增区】→ 合并为长PixelMap → 恢复原位 → 弹预览弹窗 → SaveButton保存 / 系统分享二、图片处理工具类裁剪与合并// utils/SnapshotUtil.ets import { image } from kit.ImageKit; import { UIContext } from kit.ArkUI; export class SnapshotUtil { /** * 裁剪截图中的新增滚动部分避免拼接重复 * param uiCtx UI上下文(vp2px转换) * param pm 当前屏 componentSnapshot 得到的 PixelMap * param offsets 历次滚屏Y偏移记录 [0, h1, h2...] * param vpW 截图组件宽(vp) * param vpH 截图组件可视高(vp) */ static async getCropArea( uiCtx: UIContext, pm: image.PixelMap, offsets: number[], vpW: number, vpH: number ): Promiseimage.PositionArea { const stride pm.getBytesNumberPerRow(); const buf new ArrayBuffer(pm.getPixelBytesNumber()); const area: image.PositionArea { pixels: buf, offset: 0, stride, region: { x: 0, y: 0, size: { width: 0, height: 0 } } }; if (offsets.length 2) { // 非首屏只保留本次新增滚动部分 const prevY offsets[offsets.length - 2]; const curY offsets[offsets.length - 1]; const addH curY - prevY; // 新增像素高 const cropRgn { x: 0, y: uiCtx.vp2px(vpH - addH), // pm底部addH区域 size: { width: uiCtx.vp2px(vpW), height: uiCtx.vp2px(addH) } }; await pm.crop(cropRgn); area.region cropRgn; } else { // 首屏保留全部 area.region { x: 0, y: 0, size: { width: uiCtx.vp2px(vpW), height: uiCtx.vp2px(vpH) } }; } pm.readPixelsSync(area); return area; } /** * 按序合并裁剪区域为一张长图 PixelMap * param uiCtx UI上下文 * param areas 裁剪PositionArea数组顺序滚屏顺序 * param totalContentHVP 内容总高vp 末次Y偏移 首屏可视高 * param vpW 内容宽vp */ static async mergeToLong( uiCtx: UIContext, areas: image.PositionArea[], totalContentHVP: number, vpW: number ): Promiseimage.PixelMap { const totalH uiCtx.vp2px(totalContentHVP); const w uiCtx.vp2px(vpW); const opts: image.InitializationOptions { editable: true, pixelFormat: image.PixelFormat.RGBA_8888, size: { width: w, height: totalH } }; const longPm image.createPixelMapSync(opts); let offY 0; for (const a of areas) { a.offset offY; longPm.writePixelsSync(a); offY a.region.size.height; } return longPm; } /** 简易延时 */ static sleep(ms: number): Promisevoid { return new Promise(r setTimeout(r, ms)); } }为什么只保留新增部分每次滚屏后componentSnapshot.get()截的是当前可视区全图直接拼接上下相邻内容会重叠。只 crop 新增滚入区域可保证长图无缝。三、AI推荐卡片页——滚屏截图预览保存// pages/AIRecCardPage.ets import { componentSnapshot } from kit.ArkUI; import { image } from kit.ImageKit; import { photoAccessHelper } from kit.MediaLibraryKit; import { fileIo } from kit.CoreFileKit; import { SnapshotUtil } from ../utils/SnapshotUtil; import { common } from kit.AbilityKit; // 模拟推荐商品 interface RecItem { id: string; name: string; price: string; score: number; img: Resource; } const REC_ITEMS: RecItem[] [ { id:R01, name:HarmonyOS 6 智慧耳机 Pro, price:¥899, score:4.8, img:$r(app.media.ic_headphone) }, { id:R02, name:氮化镓快充套装 120W, price:¥149, score:4.6, img:$r(app.media.ic_charger) }, { id:R03, name:AI翻译词典笔 2代, price:¥259, score:4.9, img:$r(app.media.ic_dict) }, ]; Entry Component struct AIRecCardPage { private ctx this.getUIContext().getHostContext() as common.UIAbilityContext; private scroller: Scroller new Scroller(); private REC_AREA_ID ai_rec_area; State snapshotPm: image.PixelMap | undefined undefined; State showPreview: boolean false; // 滚屏历史 裁剪区 private offsets: number[] []; private areas: image.PositionArea[] []; // 布局常量可用getInspectorBounds动态取更准 private AREA_W_VP 360; private AREA_H_VP 420; // 执行快照 async doSnapshot() { // 记住原位 const bakY this.scroller.currentOffset().yOffset; // 滚回顶部等渲染 this.scroller.scrollTo({ yOffset: 0, animation: false }); await SnapshotUtil.sleep(300); this.offsets [0]; this.areas []; await this.captureLoop(); // 恢复原位置 this.scroller.scrollTo({ yOffset: bakY, animation: { duration: 200 } }); this.showPreview true; } private async captureLoop(): Promisevoid { const pm await componentSnapshot.get(this.REC_AREA_ID); const area await SnapshotUtil.getCropArea( this.getUIContext(), pm, this.offsets, this.AREA_W_VP, this.AREA_H_VP ); this.areas.push(area); if (!this.scroller.isAtEnd()) { const nextY this.offsets[this.offsets.length - 1] this.AREA_H_VP; this.scroller.scrollTo({ yOffset: nextY, animation: { duration: 200 } }); await SnapshotUtil.sleep(350); this.offsets.push(nextY); return this.captureLoop(); } // 合并 const totalH this.offsets[this.offsets.length - 1] this.AREA_H_VP; this.snapshotPm await SnapshotUtil.mergeToLong( this.getUIContext(), this.areas, totalH, this.AREA_W_VP ); } // 保存相册配合SaveButton async saveToAlbum(pm: image.PixelMap) { try { const helper photoAccessHelper.getPhotoAccessHelper(this.ctx); const uri await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, png); const file await fileIo.open(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); const packer image.createImagePacker(); const data await packer.packToData(pm, { format: image/png, quality: 100 }); fileIo.writeSync(file.fd, data); fileIo.closeSync(file.fd); this.getUIContext().getPromptAction().showToast({ message: 已保存到相册 }); this.showPreview false; } catch (e) { this.getUIContext().getPromptAction().showToast({ message: 保存失败 }); console.error(save err: ${JSON.stringify(e)}); } } build() { Stack() { Column() { // ---- AI推荐区待截图---- Scroll(this.scroller) { Column({ space: 16 }) { Text(为你推荐 · AI精选) .fontSize(18) .fontWeight(FontWeight.Bold) .padding({ top: 12, bottom: 4 }) ForEach(REC_ITEMS, (it: RecItem) { Row({ space: 12 }) { Image(it.img).width(80).height(80).borderRadius(8).objectFit(ImageFit.Cover) Column() { Text(it.name).fontSize(15).fontColor(#333).maxLines(1) Text(it.price).fontSize(14).fontColor(#FF5722).margin({ top: 4 }) Row({ space: 4 }).margin({ top: 4 }) { ForEach(new Array(Math.round(it.score)).fill(0), (_i,i){ Image($r(sys.media.ohos_ic_public_star_filled)) .width(14).height(14).fillColor(#FF9800) }) } }.layoutWeight(1).alignItems(HorizontalAlign.Start) } .padding(12) .backgroundColor(Color.White) .borderRadius(12) .shadow({ radius: 3, color:rgba(0,0,0,0.05), offsetX:0, offsetY:1 }) }) Text(由 HarmonyOS AI 生成 · 仅供参考) .fontSize(11) .fontColor(#BBB) .margin({ top: 8, bottom: 16 }) } .padding(16) } .id(this.REC_AREA_ID) .layoutWeight(1) // ---- 底部操作栏 ---- Row() { Button(分享推荐快照) .layoutWeight(1) .height(44) .backgroundColor(#FF5722) .borderRadius(22) .onClick(() this.doSnapshot()) } .padding(12) .backgroundColor(#FFF) } .width(100%) .height(100%) .backgroundColor(#F5F6F8) // ---- 预览弹窗 ---- if (this.showPreview this.snapshotPm) { this.buildPreview() } } } Builder buildPreview() { Column() { // 遮罩 Column().layoutWeight(1).backgroundColor(rgba(0,0,0,0.45)).onClick((){ this.showPreviewfalse; this.snapshotPmundefined; }) Column() { Scroll() { Image(this.snapshotPm!).width(100%).objectFit(ImageFit.Contain) }.height(70%) Row({ space: 20 }) { Button(取消).onClick((){ this.showPreviewfalse; this.snapshotPmundefined; }) // ✅ 安全控件保存——无需 WRITE_MEDIA 权限 SaveButton({ icon: SaveIconStyle.FULL_FILLED, text:保存, buttonType:ButtonType.NORMAL }) .onClick(async (_e, res) { if (res SaveButtonOnClickResult.SUCCESS this.snapshotPm) { await this.saveToAlbum(this.snapshotPm); } }) }.padding(16) } .backgroundColor(Color.White) .borderRadius({ topLeft:24, topRight:24 }) } .width(100%) .height(100%) .position({ x:0,y:0 }) } }四、避坑指南问题原因修复截图只截到首屏componentSnapshot.get()只截组件当前渲染像素用Scroller分段滚 多次get()长图有重复段落每次截全可视区直接拼接PixelMap.crop()只保留新增滚动部分参考getCropArea保存时报权限拒绝普通 Button 调 MediaLibrary 写文件用SaveButton安全控件系统弹授权框Web图文详情截不全未启用全页绘制调webview.WebviewController.enableWholeWebPageDrawing() 等onPageEnd后截图滚屏截到动画残影scrollTo动画未完成即截图await sleep(300~500ms)等渲染完再componentSnapshot.get()五、总结AI卡片快照分享SOP给截图区组件设唯一id包在Scroll内滚回顶部 →componentSnapshot.get(id)截首屏 →crop存首段循环scrollTo(y可视高) → sleep → get → crop新增区 → push直到isAtEnd()合并createPixelMapSync 按序writePixelsSync(area)预览 SaveButtonphotoAccessHelper.createAssetImagePacker.packToDatafileIo.write核心法则HarmonyOS 6 中富媒体卡片长截图分享 componentSnapshot分段截 PixelMap.crop去重拼接 SaveButton安全落盘Web详情额外开enableWholeWebPageDrawing()。©著作权归作者所有如需转载请注明出处否则将追究法律责任。
企业数字化 ERP 产品动态
相关推荐
M2.7自反馈架构:大模型元认知能力的技术实现 1. 项目概述:这不是一次常规模型评测,而是一次“自我指涉”能力的临界点观测“MiniMax M2.7深度评测:当 AI 开始‘进化自己’,一个新时代悄悄开始了”——这个标题里藏着三个关键信号:MiniMax是主体,M2.7是… · 2026/9/25 8:20:36
轻松掌握网络监控器1.28.4高级版,高效管理网络 # 轻松掌握网络监控器1.28.4高级版,高效管理网络在当今数字化办公和智能家居环境中,网络稳定性直接影响工作效率和生活质量。无论是企业IT管理员,还是家庭网络发烧友,都曾遇到过网络卡顿、设备掉线或带宽占用异常等问题。此时&… · 2026/9/26 23:50:20
Microsoft Coreutils:让Linux命令在Windows上原生运行 引言
对于习惯在Linux或macOS终端中使用ls、grep、find等命令的开发者来说,切换到Windows命令提示符(CMD)或PowerShell环境时,常常会感到不便。虽然Windows Subsystem for Linux (WSL) 提供了完整的Linux环境,但对于一… · 2026/9/25 12:04:16
别再乱买seo优化推广软件了这3个最佳实践省一半钱 别再乱买seo优化推广软件了这3个最佳实践省一半钱 改个需求建站公司拖一周,这是多少中小老板的噩梦?你急得抓耳挠腮,对方却在群里回个“正在处理”,这种被动等待的日子,靠花钱买所谓的“seo优化推广软件”能解决吗?别天真了。软件只是工具,不懂… · 2026/9/27 14:34:42
网站一直百度上搜不到是怎么回事:3个排查步骤+对比评测 网站一直百度上搜不到是怎么回事:3个排查步骤+对比评测 刚把新站丢到百度里搜,半天没动静?别急,先看看是不是备案流程卡住了,或者服务器配置出了岔子。我见过太多新手因为搞不清备案流程一头雾水,导致网站权重被降,这时候盲目做SEO全是无用功。今… · 2026/9/27 14:34:30
湖北建设网站信息查询中心从零搭建:搞定备案不迷路 湖北建设网站信息查询中心从零搭建:搞定备案不迷路 第一次接到“湖北建设网站信息查询中心”这个需求时,我脑子里蹦出的第一个念头不是技术多难,而是 备案流程一头雾水… · 2026/9/27 14:34:30
织梦网站模板怎么做完整流程含安全加固实战 织梦网站模板怎么做完整流程含安全加固实战 域名服务器搞不懂,是很多新手接手织梦(DedeCMS)项目时最头疼的坑。你以为买了个便宜模板,上传就能用,结果后台打不开、前台报错,甚至被黑客植入了暗链,这时候才意识到: 织梦网站模板怎么做… · 2026/9/27 14:34:23
wordpress头部工具栏优化5大注意事项与源码解析 wordpress头部工具栏优化5大注意事项与源码解析 刚接到个急活,客户急着上线外贸站,结果后台一打开,那个熟悉的灰色条状物——WordPress 默认头部工具栏(Admin… · 2026/9/27 14:33:57
网站开发与建设主要干什么?揭秘费用明细与免费工具避坑指南 网站开发与建设主要干什么?揭秘费用明细与免费工具避坑指南 网站做好了没人访问,这是90%企业老板上线后最头疼的噩梦。别急着怪算法,先看看你手里的钱是不是花错了地方。很多甲方拿着预算单,心里没底,不知道 网站开发与建设主要干什么… · 2026/9/27 14:33:57
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01