ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HarmonyOS 7 SlideDrop:Precise Share目标窗口与触碰坐标

HarmonyOS 7 SlideDrop:Precise Share目标窗口与触碰坐标 QuickDock 系列已经把闪控窗做完这次我换一条完全不同的主线HarmonyOS 7 的“碰一碰·精准分享”。HarmonyOS 7 当前官方能力说明里对这项能力的描述很具体手机轻触电脑或平板屏幕系统可以识别目标窗口和触碰坐标把图片等素材精准插入指定位置。它和普通“分享给某个设备”最大的区别是接收端不只是知道“有东西来了”还知道用户碰的是哪个窗口、窗口里的哪个位置。我把 Demo 定成SlideDrop。它是一块平板上的素材板。用户在手机里选一张图轻触平板上 SlideDrop 的某个槽位希望素材不是进入“收件箱”而是直接落到用户碰到的那个区域。这个系列固定 6 篇01 目标窗口、触碰坐标与投递槽位锁定 02 UDMF 多样式素材封装、幂等提交与重复触碰去重 03 缩放 / 滚动后的坐标换算与布局变更 04 大文件延迟加载、传输失败与断点恢复 05 跨设备 Session 恢复、离线重放与冲突处理 06 多设备回归、传输时延与一致性验收第一篇只做最基础但最容易写错的一件事把系统给到的目标窗口和窗口坐标稳定解析成 SlideDrop 自己的业务槽位。本轮统一数据taskId: tap_20261002_01 sender: Phone receiver: Tablet · Landscape targetWindowId: slidedrop_board_01 viewport: 1200 × 750vp boardRect: x240 y96 w840 h560 touchPoint: x846 y392 normalized: x0.705 y0.523 targetSlot: slot_03 payload: image_20261002_17.jpg payloadSize: 2.8MB resolveCost: 6ms transferCost: 148ms status: TARGET_LOCKED一、精准投递首先不是“传文件”而是“定位目标”刚开始做 SlideDrop 时我下意识把问题理解成手机 → 平板 → 传一张图片这样做出来的结果和普通设备分享没有本质区别。真正的“精准”来自另一条信息targetWindow touchPoint用户碰的是平板上的 SlideDrop而不是平板这个设备本身碰的是 SlideDrop 里的某个视觉区域而不是应用的一个抽象接收入口。所以第一篇没有先写 UDMF也没有先写文件落盘。先定义业务侧事件exportinterfacePreciseShareEvent{taskId:stringtargetWindowId:stringtouch:{x:numbery:number}payloadId:string}exportinterfaceBoardRect{x:numbery:numberwidth:numberheight:number}exportinterfaceDropZone{id:stringrect:{x:numbery:numberwidth:numberheight:number}}这里的PreciseShareEvent是 SlideDrop 自己的适配模型不是我虚构的 HarmonyOS 官方 API 名。系统“碰一碰·精准分享”能力如何把目标窗口、触碰坐标和数据交给应用以当前官方能力页、最佳实践和示例代码为准业务层只接收一个稳定的项目模型后面系统接口调整时不把页面逻辑一起拖着改。二、先确认 targetWindowId再算槽位精准分享最危险的一种错误是只看坐标。假设平板上同时有SlideDrop 相册 文件管理相同的(846, 392)在不同窗口里根本不是同一个业务位置。所以解析入口先做目标窗口校验exportclassTargetWindowResolver{privatereadonlyboardWindowIdslidedrop_board_01accepts(event:PreciseShareEvent):boolean{return(event.targetWindowIdthis.boardWindowId)}}如果不是slidedrop_board_01直接拒绝业务解析而不是拿这组坐标硬套 SlideDrop 的 BoardRect。这一条看起来很保守但它能避免多窗口场景里最难排查的“坐标是对的窗口错了”。三、窗口坐标必须先转换成 Board 本地坐标本轮系统事件给到touch: 846 / 392SlideDrop Board 在窗口里的区域x240 y96 w840 h560所以不能直接用846 / 392去匹配 Board 内的 slot。先转换localX 846 - 240 606 localY 392 - 96 296代码exportclassDropZoneResolver{resolve(event:PreciseShareEvent,boardRect:BoardRect,zones:DropZone[]):string|null{constlocalXevent.touch.x-boardRect.xconstlocalYevent.touch.y-boardRect.yif(localX0||localY0||localXboardRect.width||localYboardRect.height){returnnull}for(constzoneofzones){constrzone.rectif(localXr.xlocalXr.xr.widthlocalYr.ylocalYr.yr.height){returnzone.id}}returnnull}}这一层解决的是窗口坐标 → 业务局部坐标后面第三篇做缩放、滚动时还会继续演进。四、slot 的几何定义来自当前布局不在代码里到处写 ifSlideDrop 当前 Board 不是平均四格。右上 slot_03 给图片预览留得更高所以本轮几何配置slot_01: x0 y0 w420 h320 slot_02: x0 y320 w420 h240 slot_03: x420 y0 w420 h320 slot_04: x420 y320 w420 h240local606/296命中slot_03页面不要写if x 660 y 400 → slot_03因为一旦布局调整这些魔法数字会立刻失效。我把几何状态收进BoardGeometryStoreexportclassBoardGeometryStore{privateboard:BoardRect{x:240,y:96,width:840,height:560}privatezones:DropZone[][]setBoard(rect:BoardRect):void{this.boardrect}setZones(zones:DropZone[]):void{this.zoneszones}snapshot():{board:BoardRect zones:DropZone[]}{return{board:{...this.board},zones:this.zones.map((item)({id:item.id,rect:{...item.rect}}))}}}业务解析只消费 Snapshot。五、normalized 坐标用于日志和后续恢复不替代真实几何判断本轮还记录normalized: 0.705 / 0.523它来自整个接收窗口846 / 1200 0.705 392 / 750 ≈ 0.523为什么还要记这个值因为后面第三篇会处理接收端窗口尺寸变化 Board 缩放 不同平板分辨率归一化坐标能帮助我们判断用户触碰意图大概位于窗口的哪个比例区域。但当前命中 slot 仍然使用真实 BoardRect 和 DropZone 几何。不能用normalized 0.5直接替代组件位置。六、TARGET_LOCKED 和 TRANSFER_DONE 是两个状态第一篇的数据里有resolveCost6ms transferCost148ms但最终状态写的是TARGET_LOCKED这是我刻意区分的。完整流程RECEIVED → WINDOW_MATCHED → TARGET_LOCKED → TRANSFERRING → COMMITTED第一篇重点是目标解析。所以诊断 UI 把TARGET_LOCKED当成核心验收结果同时记录一次 2.8MB 图片传输耗时 148ms为第二篇后面的真正提交做基线。状态机exporttypePreciseDropStateRECEIVED|WINDOW_MATCHED|TARGET_LOCKED|TRANSFERRING|COMMITTED|REJECTEDexportclassPreciseShareCoordinator{privatestate:PreciseDropStateRECEIVEDlockTarget(slotId:string):void{if(this.state!WINDOW_MATCHED){return}this.targetSlotslotIdthis.stateTARGET_LOCKED}}这样出了问题时能明确知道没找到目标还是传输没完成七、DevEco 图里看的是“窗口 坐标 槽位”三者是否一致开发图统一 HiLogtaskIdtap_20261002_01 targetWindow slidedrop_board_01 touch 846 / 392 boardRect 240 / 96 / 840 / 560 normalized 0.705 / 0.523 targetSlot slot_03 resolveCost 6ms payload image_20261002_17.jpg transferCost 148ms status TARGET_LOCKED如果只看到图片传过去了还不能证明“精准投递”成立。必须同时看到系统目标窗口 业务 Board 最终 slot三层一致。八、运行图证明的不是“有图片”而是“图片知道该去哪”最终运行图SlideDrop 板上slot_03被明确高亮。触点846 / 392右侧诊断数据仍然是同一套slidedrop_board_01 1200 × 750vp slot_03 2.8MB 6ms 148ms TARGET_LOCKED这张图真正验证的是用户碰到的地方和 SlideDrop 最终锁定的业务槽位一致。九、为什么第一篇不直接写“收到坐标就插入图片”因为投递最终一定会遇到重复碰 多条记录 大文件 异步加载 传输失败 重放如果坐标一解析成功就直接修改 Board 数据很快会出现同一次碰触写两遍 传输一半 Board 已经改了 重试又插入一份所以第一篇停在TARGET_LOCKED第二篇再把素材封装 传输 提交 幂等做完整。十、系统能力和业务适配层要保持清楚边界HarmonyOS 7 当前官方能力页明确说明“碰一碰·精准分享”可以识别目标窗口和触碰坐标。SlideDrop 不会自己伪造一套“系统精准分享协议”。业务侧只维护PreciseShareAdapter PreciseShareEvent TargetWindowResolver DropZoneResolver其中 Adapter 对接当前官方示例与系统能力后面业务代码只依赖项目模型。这和前面几条系列一直采用的思路一致系统 API 留在 Adapter工程主线保留稳定语义。十一、第一篇最后固定六组反向测试第一组正确窗口 正确坐标命中slot_03。第二组窗口 ID 不匹配直接 REJECT不继续算 slot。第三组触点落在 BoardRect 外返回 null。第四组触点压在两个 slot 边界时按当前 zone 优先级只命中一个。第五组BoardRect 更新后重新解析不读取旧布局。第六组日志里的 normalized 和原始触点可相互校验。这六组都通过以后TARGET_LOCKED才有意义。十二、下一篇真正困难的是“同一次碰触只能提交一次”第二篇会沿用同一个slidedrop_board_01但换到slot_04。一次触碰会携带两种内容image_20261002_18.png caption_20261002_18.txt并且我会故意在 800ms 内模拟第二次相同触碰事件。目标不是简单“拒绝重复按钮点击”而是保证同一个 session 同一份 payload 同一个 target slot 只提交一次这也是精准投递真正进入生产链路必须解决的问题。十三、窗口坐标不是永远等于应用内容坐标做到第一篇时我专门留了一条限制windowX / windowY 不能被长期当成业务坐标当前 BoardRect 直接放在主窗口里所以只需要减掉board.x board.y就能得到局部坐标。但真实工程后面还会出现系统栏 页面标题区 缩放容器 滚动偏移 嵌套 Navigation 分屏后的窗口区域变化只要其中一项加入“窗口坐标减 BoardRect”就可能不够。所以DropZoneResolver现在没有直接访问页面组件而是只消费BoardGeometryStore.snapshot()。第三篇会把这个 Snapshot 升级成完整变换链Window Coordinate → Content Coordinate → Board Coordinate → Zoom / Scroll Transform → DropZone第一篇先把入口隔离出来是为了以后修改坐标链时不影响 PreciseShareAdapter。十四、BoardGeometryStore 的更新时机必须和 UI 布局保持一致另一个容易出现的问题是页面已经重新布局 GeometryStore 还是旧值比如平板进入分屏以后Board 从840 × 560缩成更窄区域。如果 Store 还保留旧 rect下一次碰一碰会解析到错误 slot。所以 SlideDrop 约定布局完成 → 更新 BoardRect slot 尺寸变化 → 更新 DropZone PreciseShareEvent 到达 → 只读当前 Snapshot不允许解析过程中临时去测量某一个组件。这样事件链更确定。为了避免“半更新”BoardRect 和 Zones 还会带同一个geometryVersion只有版本一致时才允许解析。如果board version12 zones version11当前事件直接返回GEOMETRY_NOT_READY比把内容投错位置更安全。十五、边界点要定义唯一归属规则四个槽位之间一定会有边界。例如用户刚好碰在slot_01 / slot_03分界线上。如果两个判断都使用 左边 右边同一个点可能同时命中两个 zone。第一篇最终使用的是左闭右开 上闭下开只有 Board 最右侧和最下侧允许闭区间。这样x420会稳定归到右侧 slot而不会被左右两个区域同时命中。这个规则必须在 Resolver 里统一不能每个 slot 自己判断。否则布局一复杂边界触碰会成为很难复现的偶发错误。十六、目标锁定后还要保存一次 Geometry Snapshot从TARGET_LOCKED到真正开始传输之间可能有几十毫秒。这段时间里页面如果滚动或重新布局slot_03 的位置可能发生变化。所以锁定目标时不仅保存slotId还保存geometryVersion boardRect slotRect lockTimestamp第二篇 Commit 前再比较一次版本。如果版本已经变了重新校验目标而不是拿旧坐标继续提交。这也是为什么我没有在第一篇直接把图片写入 Board。“目标锁定”本质上是一份有时间边界的业务上下文不是永久有效的对象引用。十七、窗口切换场景必须拒绝旧事件PC / 平板上可能同时打开多个 SlideDrop 窗口。用户第一次碰slidedrop_board_01然后很快切到另一个窗口。如果旧事件延迟到达接收端必须继续使用事件里的targetWindowId而不是当前焦点窗口来决定投递目标。这是系统精准分享提供“目标窗口”信息真正重要的地方。应用层不能把它简化成谁现在在前台就给谁。否则一到多窗口就失去“精准”的语义。十八、第一篇的 6ms 不是整条分享耗时resolveCost6ms只包含窗口校验 坐标换算 zone 命中 目标状态写入它不包含媒体读取 跨设备传输 接收端落盘 图片解码 最终渲染所以同一篇里还单独记录transferCost148ms这种口径拆分会在后续性能回归里继续保留。如果以后总耗时变慢才能明确知道是目标解析变慢还是数据通路变慢而不是只看到一个“大约 200ms”。十九、精确命中失败时产品上应该允许用户重新定位精准能力不能把“没命中”做成死路。当事件返回OUTSIDE_BOARD GEOMETRY_NOT_READY NO_DROP_ZONESlideDrop 会保留素材不立即丢弃然后提供重新定位入口。用户可以再次碰触目标区域。这和普通分享“发送一次就结束”不一样。精准投递的失败很多时候只是目标位置无效不代表素材或设备连接本身有问题。把目标解析失败和传输失败分开用户恢复路径会清楚很多。二十、这套坐标模型为什么值得先做一整篇从功能演示角度直接把图片落到 slot_03 更快。但工程上后面所有能力都依赖目标窗口 触点 Board Slot四层关系稳定。如果第一篇没有把它做成独立 Resolver后面加入缩放 滚动 大文件 重试 会话恢复每一篇都会重复出现“到底投到哪个槽位”的问题。所以这一篇真正建立的是 SlideDrop 的投递坐标基础设施。后面再怎么扩能力业务提交最终仍然只接收一个明确结果targetSlot而不需要知道系统触点是怎么来的。参考资料HarmonyOS 7 全场景能力碰一碰·精准分享https://developer.huawei.com/consumer/cn/features/all-scenarioHarmonyOS 7 新能力一览https://developer.huawei.com/consumer/cn/features/ArkUI 自定义事件分发与坐标信息https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/api/ts-universal-attributes-on-child-touch-test
返回列表