还记得今年 6 月 HDC 2026 上,有个演示视频让我印象挺深:手机轻触电脑或平板屏幕,系统不止完成跨设备分享,还能识别目标窗口和触碰坐标,把手机里的图片、素材精准插到电脑或平板的指定位置。
以前聊“碰一碰分享”,更多是说“把文件从 A 设备送到 B 设备”。但 HarmonyOS 7(API 26)开始,轻碰事件里带的坐标信息把这事儿往前推了一步:它不光知道“传给谁”,还知道“碰到了哪里”。这意味着应用可以把一次分享变成一个明确的业务动作——比如把图片插到文档光标附近、把素材投到画布坐标点、或者替换窗口里某个被碰区域的内容。
这篇文章我们就来体验一下碰一碰·精准分享:先跑通基础的碰一碰文件分享,再在电脑/平板接收侧读轻碰坐标,最后把手机分享过来的图片插到目标位置。
一、从 HDC 2026 的演示说起:碰一碰不只是传文件
HDC 2026 现场那个“碰一碰精准分享”的演示,交互链路非常短,用户不需要经历传统文件流转里的“发送、接收、打开、拖拽、插入”,实现了我想把这张图放这儿,手机触碰对应位置就可以分享了。
从开发角度看,背后其实是两段能力拼起来的:
碰一碰文件分享:用 Share Kit 把手机端图片、视频、文件传到目标设备
轻碰坐标获取:在电脑或平板侧,从回调事件里拿到基于屏幕左上角的 screenX、screenY
letreceivableTargetInfo = receivableTarget.getInfo(); letscreenX = receivableTargetInfo.coordinate?.screenX; letscreenY = 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) =>{
letmainWindowID:number= data.getWindowProperties().id;
harmonyShare.on('knockShare', {windowId: mainWindowID },(target: harmonyShare.SharableTarget) =>{
this.immersiveCallback(target);
});
});
*可左右滑动查看
4. 组装 SharedData 并分享
轻碰触发后,发送端从 AppStorage 里拿到已选文件,逐个转成 SharedRecord,再组装成 SharedData:
publicimmersiveCallback(target: harmonyShare.SharableTarget){
letfileShare = AppStorage.get('KnockFileShare_fileShare')asnumber[];
letvideoDataList = AppStorage.get('KnockFileShare_videoDataList')asFileData[];
if(!fileShare || fileShare.length ===0) {
return;
}
letshareData: systemShare.SharedData =
newsystemShare.SharedData(this.getShareRecord(videoDataList[fileShare[0]]));
for(leti =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)版本开始,手机与电脑、手机与平板触发轻碰事件时,在 电脑或平板侧可以从回调事件里获取轻碰位置。坐标以屏幕左上角为原点:
letreceivableTargetInfo = receivableTarget.getInfo(); letscreenX = receivableTargetInfo.coordinate?.screenX; letscreenY = receivableTargetInfo.coordinate?.screenY;
*可左右滑动查看
发送侧也能从 SharableTarget.getInfo() 拿到坐标,但本文场景里插入动作发生在 电脑/平板侧,所以重点看接收侧。
1. 在 dataReceive 回调里先缓存落点
可以把接收回调改成这样:
privatedataReceiveCallback =(receivableTarget: harmonyShare.ReceivableTarget) =>{
lettargetInfo = receivableTarget.getInfo();
letscreenX = targetInfo.coordinate?.screenX;
letscreenY = 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 的矩形
functiontoEditorPoint(screenX, screenY, windowRect, editorRect){
constwindowX = screenX - windowRect.left;
constwindowY = screenY - windowRect.top;
return{
x: windowX - editorRect.left,
y: windowY - editorRect.top
};
}
*可左右滑动查看
换算完还要做边界判断:
functionisInRect(point, rect): boolean {
returnpoint.x>=0&&
point.y>=0&&
point.x<= rect.width &&
point.y <= rect.height;
}
*可左右滑动查看
这一步决定业务体验。坐标落在编辑区,就插图片;落在工具栏,就弹导入菜单;落在侧边栏,就加到素材库。
3. 把收到的图片插到指定位置
收到文件后,从 sharedData.getRecords() 拿到记录,对每条有效 uri 调用插入逻辑。核心是下面这个函数:
privateinsertImageFromKnock(uri: string, screenX: number, screenY: number) {
consteditorPoint = 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 演示后,第一时间想把它跑通的原因。
*文中代码示例仅用于演示核心逻辑,实际开发请结合业务具体需求进行调整。
