还记得今年 6 月 HDC 2026 上,有个演示视频让我印象挺深:手机轻触电脑或平板屏幕,系统不止完成跨设备分享,还能识别目标窗口和触碰坐标,把手机里的图片、素材精准插到电脑或平板的指定位置。
以前聊“碰一碰分享”,更多是说“把文件从 A 设备送到 B 设备”。但 HarmonyOS 7(API 26)开始,轻碰事件里带的坐标信息把这事儿往前推了一步:它不光知道“传给谁”,还知道“碰到了哪里”。这意味着应用可以把一次分享变成一个明确的业务动作——比如把图片插到文档光标附近、把素材投到画布坐标点、或者替换窗口里某个被碰区域的内容。
这篇文章我们就来体验一下碰一碰·精准分享:先跑通基础的碰一碰文件分享,再在 电脑/平板接收侧读轻碰坐标,最后把手机分享过来的图片插到目标位置。
一、从 HDC 2026 的演示说起:碰一碰不只是传文件
HDC 2026 现场那个“碰一碰精准分享”的演示,交互链路非常短,用户不需要经历传统文件流转里的“发送、接收、打开、拖拽、插入”,实现了我想把这张图放这儿,手机触碰对应位置就可以分享了。
从开发角度看,背后其实是两段能力拼起来的:
碰一碰文件分享:用 Share Kit 把手机端图片、视频、文件传到目标设备
轻碰坐标获取:在电脑或平板侧,从回调事件里拿到基于屏幕左上角的 screenX、screenY
let receivableTargetInfo = receivableTarget.getInfo();let screenX = receivableTargetInfo.coordinate?.screenX;let screenY = receivableTargetInfo.coordinate?.screenY;*可左右滑动查看
拿到坐标之后,分享就不再是“扔进下载目录”。它可以进入编辑器、文档、画板、白板、看板这些更具体的业务位置。
二、工程利旧:画板应用迭代升级思路
我们基于一个6.0版本的demo进行更新适配,最终完成我们的迭代体验。
entry/src/main/ets├── controller│ └── KnockController.ets # 碰一碰分享与接收核心控制器├── entryability│ └── EntryAbility.ets # 应用入口、页面加载、窗口断点更新├── model│ └── FileData.ets # 文件数据模型和示例资源├── pages│ └── Index.ets # 首页、文件初始化、选择列表、监听注册└── utils ├── BreakpointSystem.ets # 响应式断点工具 └── FileUtil.ets # MIME 判断、视频缩略图生成*可左右滑动查看
1. 三个关键改造点
要按 HarmonyOS 7(API 26)来做精准坐标插图,得关注三个改造点。
(1)版本能力要对齐。原示例配置里可能还是 6.0.0(20),要用 HarmonyOS 7(API 26)的轻碰坐标能力,需要把 SDK、DevEco Studio 和设备系统能力都切到对应版本。
(2)要支持平板,需要在模块设备类型里补上 tablet:
"deviceTypes": [ "phone", "2in1", "tablet"](3)业务层要从“接收后展示文件列表”升级成“接收后按坐标插入”。这就需要在接收端维护一个目标区域,比如文档编辑区、画布区域,或者一组可插入的容器。
2. 最小闭环:从选图到精准插入
我这次实操的最小闭环是:
手机端选图 -> 轻碰电脑/平板屏幕 -> Share Kit 传图 -> 电脑/平板 dataReceive 回调 -> 读 screenX/screenY -> 文件落盘到沙箱 -> 把屏幕坐标换算成窗口/画布坐标 -> 在指定位置插图片*可左右滑动查看
整条链路的关键在于最后两步:屏幕坐标到窗口/画布坐标的换算,以及在业务层把插入动作和具体位置绑定起来。不同应用的画布坐标系可能不同,需要根据实际场景做适配。
三、跑通基础链路:文件从手机分享电脑/平板
“精准插入”的前提,是文件的可靠传输。
1. 先把资源变成可分享的 URI
在 rawfile 或 media 资源里的图片和视频,不能直接拿来跨设备分享。页面启动时,Index.ets 会先执行 handleFileInit(),把资源复制到应用沙箱目录,再用 fileUri.getUriFromPath() 转成 Share Kit 能用的 URI。
有个容易踩的坑:别把 rawfile/media 的资源名直接塞给 SharedRecord.uri——Share Kit 要的是接收端能处理的文件 URI,不是工程内的资源标识。
2. 页面勾选文件,AppStorage 存分享状态
页面把文件列表和已选文件索引放在 AppStorage 里,用两个 @StorageLink 分别绑定 KnockFileShare_videoDataList 和 KnockFileShare_fileShare。勾选时只存文件下标,不直接拼分享数据。
这么做的优势是,页面只负责 UI 状态,真正分享时由 KnockController 统一读 fileShare 和 videoDataList。这个分层很适合继续扩展——后面要加坐标,只要在控制器回调里加就行。
3. 注册 knockShare 监听
普通设备可以直接注册 knockShare;电脑侧建议绑定 windowId,让系统知道当前窗口参与轻碰:
window.getLastWindow(this.context).then((data) => { let mainWindowID: number = data.getWindowProperties().id; harmonyShare.on('knockShare', { windowId: mainWindowID }, (target: harmonyShare.SharableTarget) => { this.immersiveCallback(target); });});*可左右滑动查看
4. 组装 SharedData 并分享
轻碰触发后,发送端从 AppStorage 里拿到已选文件,逐个转成 SharedRecord,再组装成 SharedData:
public immersiveCallback(target: harmonyShare.SharableTarget) { let fileShare = AppStorage.get('KnockFileShare_fileShare') as number[]; let videoDataList = AppStorage.get('KnockFileShare_videoDataList') as FileData[]; if (!fileShare || fileShare.length === 0) { return; }let shareData: systemShare.SharedData = new systemShare.SharedData(this.getShareRecord(videoDataList[fileShare[0]])); for (let i = 1; i < fileShare.length; i++) { shareData.addRecord(this.getShareRecord(videoDataList[fileShare[i]])); }
target.share(shareData);}
*可左右滑动查看
每个文件记录里,uri 用真实文件 URI,utd 通过扩展名推导。
做到这儿,碰一碰文件分享的发送端就跑通了。
四、接收端落盘:dataReceive 是精准插图的入口
电脑或平板要实现“收到文件并插到指定位置”,入口是 harmonyShare.on('dataReceive')。
接收监听是这样注册的:
harmonyShare.on('dataReceive', { windowId: mainWindowID, capabilities: [ {'utd': uniformTypeDescriptor.UniformDataType.MEDIA, 'maxSupportedCount': 5}, {'utd': uniformTypeDescriptor.UniformDataType.FILE, 'maxSupportedCount': 5} ]}, (receiveTarget: harmonyShare.ReceivableTarget) => { receiveTarget.receive(fileUri.getUriFromPath(this.context.filesDir), { onDataReceived: (shareData: systemShare.SharedData) => { // 处理收到的数据 }, onResult(resultCode: harmonyShare.ShareResultCode) { // 处理接收结果 } });});*可左右滑动查看
capabilities 表示接收端支持什么类型、最多收几个。比如这里只声明支持媒体和普通文件,最多 5 个。如果只做“手机传一张图到电脑指定位置”,可以收窄成图片能力:
capabilities: [ { utd: uniformTypeDescriptor.UniformDataType.IMAGE, maxSupportedCount: 1 }]*可左右滑动查看
接收后,通过 shareData.getRecords() 拿到文件记录。原示例会按类型处理缩略图,然后追加到页面列表:
let shareRecords = shareData.getRecords();shareRecords.forEach(async (record: systemShare.SharedRecord) => { if (!record.uri) return; // 图片直接用原 URI;视频生成缩略图;其他走默认缩略图 videoDataList.push({ url: record.uri, name: fileName, description: record.description, thumbnail: thumbPath, index: videoDataList.length });});*可左右滑动查看
如果目标是“文档指定位置插图”,这段逻辑可以拆成两步:
1. 文件落盘和类型处理照常保留
2. 不再只是 videoDataList.push(),而是调用第 5 节要讲的 insertImageFromKnock(uri, screenX, screenY)
也就是说,dataReceive 是文件进应用的入口,coordinate 是业务落位的入口。两者合在一起,才是精准分享。
五、精准定位:从 screenX/screenY 到指定位置插图
现在进入 HarmonyOS 7(API 26)的重点。
从 HarmonyOS 7(API 26)版本开始,手机与电脑、手机与平板触发轻碰事件时,在 电脑或平板侧可以从回调事件里获取轻碰位置。坐标以屏幕左上角为原点:
let receivableTargetInfo = receivableTarget.getInfo();let screenX = receivableTargetInfo.coordinate?.screenX;let screenY = receivableTargetInfo.coordinate?.screenY;*可左右滑动查看
发送侧也能从 SharableTarget.getInfo() 拿到坐标,但本文场景里插入动作发生在 电脑/平板侧,所以重点看接收侧。
1. 在 dataReceive 回调里先缓存落点
可以把接收回调改成这样:
private dataReceiveCallback = (receivableTarget: harmonyShare.ReceivableTarget) => { let targetInfo = receivableTarget.getInfo(); let screenX = targetInfo.coordinate?.screenX; let screenY = targetInfo.coordinate?.screenY;if (screenX === undefined || screenY === undefined) { // 兜底策略:插到当前光标、画布中心或最近一次触点 screenX = this.lastInsertX; screenY = this.lastInsertY; }
receivableTarget.receive(this.sandboxUri, { onDataReceived: (sharedData: systemShare.SharedData) => { this.handleReceivedData(sharedData, screenX, screenY); } });}
*可左右滑动查看
这里把坐标和 sharedData 绑在同一个处理函数里,避免后面异步处理时丢掉落点。
2. 屏幕坐标不能直接当组件坐标用
screenX/screenY 是屏幕坐标,图片要插入的是窗口内部的某个区域。中间至少要做一次坐标转换:
screenX/screenY -> 减去窗口左上角 windowLeft/windowTop -> 得到窗口内坐标 windowX/windowY -> 减去编辑区左上角 editorLeft/editorTop -> 得到编辑区坐标 editorX/editorY*可左右滑动查看
伪代码可以这样写:
// windowRect / editorRect 是带 left/top/width/height 的矩形function toEditorPoint(screenX, screenY, windowRect, editorRect){ const windowX = screenX - windowRect.left; const windowY = screenY - windowRect.top; return { x: windowX - editorRect.left, y: windowY - editorRect.top };}*可左右滑动查看
换算完还要做边界判断:
function isInRect(point, rect): boolean { return point.x >= 0 && point.y >= 0 && point.x <= rect.width && point.y <= rect.height;}*可左右滑动查看
这一步决定业务体验。坐标落在编辑区,就插图片;落在工具栏,就弹导入菜单;落在侧边栏,就加到素材库。
3. 把收到的图片插到指定位置
收到文件后,从 sharedData.getRecords() 拿到记录,对每条有效 uri 调用插入逻辑。核心是下面这个函数:
private insertImageFromKnock(uri: string, screenX: number, screenY: number) { const editorPoint = toEditorPoint(screenX, screenY, this.windowRect, this.editorRect); if (!isInRect(editorPoint, this.editorRect)) { this.addToMaterialPanel(uri); return; }this.images.push({ uri, x: editorPoint.x, y: editorPoint.y, width: 160, height: 120 });}
*可左右滑动查看
这里做了三件事:坐标转换、边界判断、业务落位。如果碰点落在编辑区,就把图片插到对应坐标;落在其他区域,就回退到素材面板。
这样,一个“手机轻碰电脑指定位置插图”的最小闭环就跑通了。
4. 整理核心调用链
把它放回当前工程,核心调用链可以整理成:
Index.aboutToAppear() -> handleFileInit() -> KnockController.getInstance(context) -> 电脑/平板注册 knockShare + dataReceive手机端勾选图片 -> fileShare 存已选 index -> 轻碰触发 knockShare -> SharedData 带图片 URI -> target.share(shareData)
电脑/平板侧接收 -> dataReceive 回调 -> receivableTarget.getInfo() -> 读 coordinate.screenX / screenY -> receive(sandboxUri) -> onDataReceived(sharedData) -> getRecords() -> 坐标转换 -> insertImageFromKnock(uri, x, y)
*可左右滑动查看
这条链路比单纯文件分享多了三步:读坐标、转坐标、执行业务插入。
在实操的过程中,需要注意:
(1)坐标可能为空。 真实业务不能假设它永远有值,比较稳的兜底是插当前光标位置、编辑区中心,或回退成普通文件接收。
(2)坐标是屏幕坐标。 屏幕坐标要经过窗口偏移、组件偏移、滚动偏移,才能变成业务坐标。文档编辑器尤其要留意滚动位置。
(3)能力声明要收敛。 接收端如果只做图片插入,就不要声明支持所有文件,收窄成 IMAGE + maxSupportedCount: 1,用户体验更明确,异常处理也简单。
总结
最后总结一下这次实操:
碰一碰精准分享不是一个单点 API,而是一套新的跨设备交互方式。它的基础是文件分享,核心是目标识别和坐标,最终价值落在业务动作上。
如果说传统分享解决的是“内容怎么跨设备传输”,那 HarmonyOS 7 的轻碰坐标能力解决的就是“内容应该如何场景化传递”。这也是我看完 HDC 2026 演示后,第一时间想把它跑通的原因。
*文中代码示例仅用于演示核心逻辑,实际开发请结合业务具体需求进行调整。
【来社群切磋技术,一起升 7 来!】
来源:HarmonyOS 开发者社区 本文作者:@木斯佳
声明:经授权发布,观点仅代表作者本人,不代表官方立场。
点击下方??【】,了解更多
股票
板块
用户
资讯
评论 (0)