恒信德财经网

HarmonyOS 7轻碰坐标能力尝鲜体验

还记得今年 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
 ->文件落盘到沙箱
 ->把屏幕坐标换算成窗口/画布坐标
 ->在指定位置插图片

*可左右滑动查看

整条链路的关键在于最后两步:屏幕坐标到窗口/画布坐标的换算,以及在业务层把插入动作和具体位置绑定起来。不同应用的画布坐标系可能不同,需要根据实际场景做适配。

三、跑通基础链路:文件从手机分享电脑/平板

b4cdf65a-b5c4-11f1-90a1-92fbcf53809c.jpg

“精准插入”的前提,是文件的可靠传输。

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 演示后,第一时间想把它跑通的原因。

*文中代码示例仅用于演示核心逻辑,实际开发请结合业务具体需求进行调整。

相关推荐