HarmonyOS 7 新特性4:碰一碰——双机实测轻碰坐标与 Share Kit 卡片数据准备

发布时间:2026/10/8 7:28:33
HarmonyOS 7 新特性4:碰一碰——双机实测轻碰坐标与 Share Kit 卡片数据准备 HarmonyOS 7 新特性4碰一碰——双机实测轻碰坐标与 Share Kit 卡片数据准备文章目录HarmonyOS 7 新特性4碰一碰——双机实测轻碰坐标与 Share Kit 卡片数据准备1、引言2、效果展示与诊断台结构3、Kit 与 API对称的双回调与坐标语义4、逻辑流5、实战一组准备面读数加一次双机互碰5.1 K1 注册与解绑跑通5.2 K2 数据准备链5.3 K3 能力登记5.4 K4 四段换算5.5 K5 引导资产5.6 双机互碰坐标拿到了还发现发送侧其实没有坐标6、避坑6.1 off 的不对称签名dataReceive 解绑要带 registry6.2 coordinate 是可选字段发送侧真机上就是 undefined6.3 引导目录缺失是完全静默的7、总结1、引言两台设备顶端轻轻一碰这边屏幕上的内容就出现在那边——碰一碰knock share把「分享」这件要从联系人列表里挑半天的事简化成了一次物理接触。26.0.0 给它加了个很关键的增量手机跟 PC/2in1 或者平板互碰时被碰的那一方能从事件回调里拿到轻碰坐标坐标系以屏幕左上角为原点。这个坐标的用途很实在——决定分享的数据落在对方屏幕的哪个位置而不是闷头丢到应用的默认角落。碰一碰整条链路需要两台设备但有意思的是碰之前要做的事、碰之后能解析的东西大部分一台设备就能验证。我把接口面拆成五组小实验挨个跑了一遍回调注册、数据准备、能力登记、坐标换算、引导资产能测的全测干净真正碰起来之后坐标到底长什么样、哪一侧拿得到也在手上两台真机Mate 60 Pro 配 MatePad Edge上拿到了第一手读数。环境声明HarmonyOS 7.0.0.107 (API 26) Release实测工程轻碰台com.qingkouwei.lightknockDevEco Studio 26.0.0SDK 26.0.0.105 Release手机 Mate 60 ProALN-AL80麒麟 9000S做发送侧、平板 MatePad EdgeQXS-W00麒麟 X90A做接收侧。2、效果展示与诊断台结构轻碰台是诊断台的形态这个系列里有先例轻规划冲突台、轻探能力台把能单独验证的环节都做成了一键能点的按钮每个按钮点下去读数直接显示在页面上。互碰的部分则专门做了个探针页两台设备各开一个一边注册成发送角色、一边注册成接收角色碰完读数就出来了。准备面五个按钮全跑完的效果注册成功、76B 的演示文本落到沙箱、SharedData 回读 records1、windowId65 登记和解绑都正常、坐标换算命中 true、引导目录读到 1 项。工程结构很简单一页代码加一个资产目录entry/src/main/ ├── ets/pages/KnockLabPage.ets // 诊断台K1-K5 └── resources/rawfile/ ├── knock_demo.txt // 演示数据源 └── knock_share_guide/ // 引导资产目录官方约定的目录名这里单独说一下knock_share_guide这个目录名字是官方约定死的互碰触发时系统会从这个目录里取引导动图来播。妙就妙在——目录不存在的时候什么错都不报构建正常、运行正常只是碰的时候没有引导动画。这种「静默缺失」的资产最容易漏打进去所以我把它做成了一个显式的检查项打包完读一次读不到就报警。3、Kit 与 API对称的双回调与坐标语义接口都在kit.ShareKit的 harmonyShare 命名空间里同一个 kit 下还有个 systemShare管的是分享数据本体。碰一碰的接口面是一对对称的双回调侧注册回调参数核心方法发送侧on(knockShare, cb)SharableTargetshare(SharedData)/reject()/updateShareData()接收侧on(dataReceive, registry, cb)ReceivableTargetreceive(sandboxUri, cb)/reject()两边的 target 都带一个getInfo()返回{ coordinate?: CoordinateInfo }而 CoordinateInfo 就是简单的{ screenX, screenY }。发送侧和接收侧理论上都能拿到坐标——发送侧拿到的是「对方碰在我屏幕上的映射点」接收侧拿到的是「我碰在对方屏幕上的映射点」坐标系统一是屏幕左上角原点。注意 coordinate 是可选字段coordinate?在不支持轻碰坐标的设备或者场景下会是 undefined所以取值之前必须判空——这是接口面写进签名里的第一个规矩后面会看到它在真机上意味着什么。接收侧的注册要多传一个参数RecvCapabilityRegistry { windowId: number, capabilities: [{ utd, maxSupportedCount }] }作用相当于告诉系统「我这个窗口能收哪些 UTD 类型的数据每种最多收几条」。windowId 从window.getLastWindow现取utd 用 uniformTypeDescriptor 的常量别手写字符串。数据本体是systemShare.SharedData构造时收一条SharedRecordutd 必填content/uri/title/label/description/thumbnail/thumbnailUri/extraData 都是可选后续用 addRecord 追加、getRecords 回读。官方给了两条硬约束分享数据的描述总大小不超过 200KB、记录数不超过 500。版本上有一句话得说清楚免得写错碰一碰的基础能力从 HarmonyOS 5 就有了26.0.0 新增的只是轻碰坐标——把它整个写成本版本新能力是不对的。互碰之后对方屏幕上出现的是Share Kit 卡片有三种模板纯图片、沉浸式大卡、白卡上下结构。卡片是系统渲染的应用能插手的只有喂给它的素材预览图推荐 3:4 比例、600×800 起步、最大 3000×4000——比例不对会被裁尺寸不够会糊这两个数就是素材的验收线。卡片跟坐标的关系也理一下坐标决定卡片放在屏幕哪儿模板决定卡片长什么样两件事互相独立。4、逻辑流把碰一碰从注册到触发串一遍先看准备面注册 knockShare 回调演示资产落沙箱构建 SharedData 并回读实取 windowId登记 RecvCapabilityRegistry坐标四段换算演示引导目录落包检查准备就绪等互碰真碰起来之后完整的一轮大概是这样接收侧应用系统协同服务发送侧应用接收侧应用系统协同服务发送侧应用顶端相碰硬件事件knockShare 回调(SharableTarget)dataReceive 回调(ReceivableTarget)getInfo().coordinate 取轻碰坐标share(SharedData)卡片上屏坐标处放置receive(sandboxUri, cb)onResult SHARE_SUCCESS这张图里有一行跟官方文档的印象不太一样getInfo().coordinate在发送侧真机上拿不到undefined得走判空降级——这不是推测是下面 5.6 实测出来的。坐标的「放置」语义实际只有接收侧那半边能用。5、实战一组准备面读数加一次双机互碰5.1 K1 注册与解绑跑通on(knockShare, cb)注册没有异常off(knockShare, cb)解绑也没有异常一来一回跑通。注册成功当然不等于整条链路一定可用但注册失败就一定不可用所以它是个必要的地基。回调体里我把坐标读取的逻辑提前埋好——先getInfo()判空再取 screenX/Y——这样真碰起来的时候读数会自动进日志。5.2 K2 数据准备链把 rawfile 里 76B 的演示文本用fs.openSync writeSync写进沙箱然后用utdgeneral.plain-textuniformTypeDescriptor 的常量加上 uri 构造 SharedData再用 getRecords 回读——records1、uri 还在资产到沙箱到分享结构这三段就串通了。至于 200KB/500 条那两条约束单条 76B 根本够不着构建这一步不会触发它们。5.3 K3 能力登记window.getLastWindow实际拿到 windowId65用这个值加两条能力plain-text 上限 10 条、image 上限 4 条构造 RecvCapabilityRegistryon(dataReceive, registry, cb)注册成功。解绑的时候踩了个坑值得单独说off(dataReceive)的第二个参数是 registry不是 callback——完整签名是off(event, capability, callback?)registry 必传、callback 反而可选。我一开始照着 knockShare 那种「对称」的直觉去传 callback编译直接报 overload 不匹配。教训是看着对称的两个回调on/off 的签名得一个一个对着头文件核别想当然。5.4 K4 四段换算轻碰坐标是屏幕级的但你要落到某个控件上中间隔着四段换算屏幕坐标左上原点→ 窗口坐标减掉窗口在屏幕上的偏移全屏窗口偏移是 0、浮窗就得读 windowRect→ 页面坐标再减页面在窗口里的偏移包括顶部避让区高度→ 控件命中落在哪个热区矩形里。演示里屏幕坐标 (630,900) 换算下来命中了热区 [430,700,830,1100]返回 true。这里有两个坑要特别留神。第一坐标系是屏幕左上角原点官方明确写了跟 ArkUI 组件自己那套局部坐标完全不是一回事中间哪一段换算错了放置就偏一截。第二判命中要用热区矩形别精确匹配像素点。碰这个动作的物理精度天然在厘米级你要求坐标正好落在某个点上根本不现实——官方博客也点名过像素匹配的脆弱。把误差来源拆开看更清楚第一段屏幕→窗口的误差来自取 windowRect 的时机浮窗被拖动后 windowRect 会变得在回调触发的那一刻现取、不能缓存第二段窗口→页面的误差来自不同设备形态避让区和标题栏的高度差异直板机有状态栏、PC 形态的标题栏在窗口内部pageOffY 怎么取会随形态变第三段页面→控件的误差其实不是换算误差而是热区本身划得多大的问题——热区是业务决策不是数学问题。前两段能测能修最后一段只能声明边界热区划得粗换算再准也放不准这正好是「用区域别用像素」这条的另一面。5.5 K5 引导资产getRawFileList(knock_share_guide)返回 1 项说明目录打进包了、也能读出来。真正上线时把里面的占位文件换成设计做好的动图就行目录名和读取方式都不用动。这个检查的价值不在读到了什么而在读不到的时候你能第一时间知道——毕竟系统对缺失这件事是完全沉默的。5.6 双机互碰坐标拿到了还发现发送侧其实没有坐标准备面做完手上正好有 Mate 60 Pro 和 MatePad Edge就把两台各开了一个探针页——手机做发送侧、平板做接收侧两边点「注册双角色」然后顶端一碰。这次互碰的收获比预想的大有三条先说接收侧的坐标屏幕左上角原点直接拿到手。平板的 dataReceive 回调里getInfo().coordinate返回了screen(1409,598)。碰的位置就在平板屏幕中部偏左上跟实际对得上——也就是说 K4 那条四段换算链输入端从此有了真值锚点不用再拿假想坐标演示了。后来又碰了几次读数里 (1856,723)、(1537,758) 这些值都是这么来的每次碰的落点都不一样但坐标语义一直稳定。再说发送侧一个不起眼但很关键的发现手机的 coordinate 是 undefined。连着碰了好几次手机作为发送侧每次getInfo().coordinate都走到判空那条分支去。官方文档把 coordinate 写成可选字段、只说「不支持的设备为 undefined」读起来像个边缘情况真碰过才知道手机当发送侧的时候系统就是不下发坐标。换句话说「碰哪儿就放在哪儿」这个漂亮语义实际上只在接收侧那一半成立发送侧想放置只能老老实实退到默认位置。这个不对称在文档里没有明说是碰出来的。第三条关于 resolve 的时延以及它到底代表什么。手机 share、平板 receive 的 resolve 都在 1-2ms 这个量级快得像是秒传。但毫秒级的 resolve 只代表「系统受理了这个动作」不代表文件真的传过去了。这提醒很值钱别拿 resolve 成功当传输完成的证据业务上判断「传完了」得看真正收到数据的那个回调不是看 promise resolve。平板接收侧的读数坐标真值 第一次触发 resolve 时延手机发送侧的读数coordinateundefined 判空命中 resolve 时延6、避坑6.1 off 的不对称签名dataReceive 解绑要带 registry现象解绑 dataReceive 时按 knockShare 那种写法传了个 callback 进去编译报No overload matches this call。原因on(dataReceive)注册时参数里带了 registry对应的 off 签名是off(event, capability, callback?)——registry 是必传的第二个参数callback 反而是可选的。看着对称的两个回调off 的入参却不对称。怎么办注册时就把 registry 存成成员变量解绑时直接复用双回调的 on/off 签名逐个对着头文件核别凭对称直觉写。确认换成传 registry 之后编译过了登记—解绑在真机上跑通无异常。6.2 coordinate 是可选字段发送侧真机上就是 undefined现象回调体里直接info.coordinate.screenX的写法很危险——coordinate 的类型是CoordinateInfo?可能为 undefined。原因官方文档写了这是可选字段但把它归到「不支持坐标的设备」那种边缘情况。真机互碰才看到另一面手机作为发送侧时 coordinate 每次都是 undefined系统压根没下发。这不是理论分支是常态。怎么办取坐标前先判空拿不到就走默认放置位的降级逻辑屏幕中心或应用主区域都行。设计上要接受一个事实——「碰哪儿放哪儿」这个卖点实际只有被碰的那一侧能用发起分享的一侧只能保证「碰了能放」放哪儿是应用自己定的。确认判空分支提前埋在了回调体里互碰时手机每次触发都走进这个分支并有日志读数降级路径实打实跑起来了。6.3 引导目录缺失是完全静默的现象knock_share_guide目录忘打进包的时候构建不报错、运行不报错只是碰的时候引导动图不播。原因引导资产是系统按约定目录名懒加载的取不到就跳过不抛异常。怎么办这种静默缺失的资产不能指望系统提醒你自己做启动自检——目录读一次读不到就写日志、上诊断界面。我把这个检查做成了 K5 那个按钮。确认K5 正常返回 1 项读取链路通。7、总结这一趟下来的交付物说白了是一张核对过的碰一碰接口清单对称的双回调配上不对称的 off 签名、可选的 coordinate 字段和它的屏幕左上角原点语义、registry 里 windowId 加 utd 的能力登记、SharedData 的构建与回读以及 200KB/500 条两条约束、四段坐标换算和「用区域别用像素」的落位还有引导目录那种静默资产必须自建诊断。准备面五个按钮全绿意味着真正碰起来之前该打的地基都打实了。双机互碰最值钱的收获其实不是接收侧那个坐标真值而是发送侧那个 undefined。官方文档写 coordinate 是可选字段你读一百遍「可能为 undefined」都不如真碰一次看清楚到底哪一侧拿不到。做接口适配这种事文档告诉你「有什么」真机告诉你「实际给你什么」两者对不上的地方往往才是产品的分水岭——碰一碰的「精准放置」听着很美但它其实是单边能力这个认知得在动手写业务之前就有。顺带那条毫秒级 resolve 也别被骗了它只说明系统接住了你的动作不代表数据真的到了对面。凡是「resolve 就当成功」的地方都值得回头问一句——完成真正的信号到底在哪个回调里。