资讯动态

《HarmonyOS 7 精准碰一碰跨设备协作开发实战》03:多窗口场景下的目标识别与精准路由【鸿蒙心迹】

发布时间:2026/9/27 3:48:56 来源:尧图企业网站定制
前两篇我们默认应用只有一个窗口。但PC上干活谁不是开一堆窗口素材库一个、编辑器一个、预览一个手机一碰到底传给谁上一篇写完坐标转换我自己拿PC测了一下。我同时开了三个CrossDrop窗口左边素材库中间主编辑器右边预览窗口。拿手机碰中间那个窗口的画布区域——结果呢图片跑到左边素材库去了。我当时就愣了。坐标明明算对了怎么会错窗口后来想明白了我只算了这个点在哪个区域没算这个点在哪个窗口。屏幕上有三个窗口坐标转换是相对于哪个窗口算的这个问题不解决多窗口下就是乱传。一、先搞清楚多窗口到底多在哪我们上一篇的模型是一个应用一个主窗口一个页面三个区域。但PC和2in1设备不是这样的。第一种窗口——主窗口。应用启动默认打开的那个里面是完整的工作台。第二种窗口——素材库窗口。用户可以单独开一个素材管理窗口拖到屏幕另一边。第三种窗口——预览窗口。单独开个小窗实时预览编辑效果。这三个窗口同时在屏幕上每个都有自己的位置、尺寸、内容区域。用户拿手机碰屏幕的时候系统告诉你触点在屏幕坐标(x500, y300)。你得先回答一个问题这个坐标落在哪个窗口里知道了是哪个窗口才能用那个窗口的位置去做坐标转换。知道了窗口才能判断是素材库区域、画布区域还是预览区域。你看比上一篇多了一层先选窗口再转坐标再匹配区域。二、WindowTargetRegistry给每个窗口上户口我们需要一个东西专门记录当前应用有哪些窗口、每个窗口在哪、每个窗口该怎么处理数据。我叫它WindowTargetRegistry——窗口目标注册表。它要干三件事第一件窗口创建时注册。主窗口创建了告诉注册表我在这我的位置是(x1,y1)我的业务类型是editor。素材库窗口创建了告诉注册表我在这我的业务类型是material。第二件窗口位置变化时更新。用户把窗口从左边拖到右边了注册表要更新这个窗口的最新位置。窗口大小变了也要更新。第三件窗口关闭时注销。预览窗口关了注册表要把它删掉。不然下次碰的时候还去找这个已经不存在的窗口就出问题了。这段代码解决什么问题窗口目标注册表。文件registry/WindowTargetRegistry.ets用途管理所有可接收分享的窗口接入位置窗口创建和销毁时interfaceWindowTarget{windowId:string;type:editor|material|preview;offsetX:number;offsetY:number;width:number;height:number;isActive:boolean;}classWindowTargetRegistry{privatetargets:Mapstring,WindowTargetnewMap();// 注册窗口register(target:WindowTarget){this.targets.set(target.windowId,target);console.info(注册窗口target.type at (target.offsetX,target.offsetY));}// 更新窗口位置updatePosition(windowId:string,x:number,y:number,w:number,h:number){lettargetthis.targets.get(windowId);if(target){target.offsetXx;target.offsetYy;target.widthw;target.heighth;}}// 注销窗口unregister(windowId:string){this.targets.delete(windowId);console.info(注销窗口windowId);}// 根据屏幕坐标找落在哪个窗口里findWindowAt(screenX:number,screenY:number):WindowTarget|null{for(lettargetofthis.targets.values()){if(screenXtarget.offsetXscreenXtarget.offsetXtarget.widthscreenYtarget.offsetYscreenYtarget.offsetYtarget.height){returntarget;}}returnnull;}}三、完整路由链从触点到业务处理器现在我们有了窗口注册表。一条完整的路由链就清楚了第一步触点 → 找窗口。屏幕坐标(x500, y300)问注册表这个点落在哪个窗口注册表遍历所有窗口发现落在编辑器窗口里。第二步窗口 → 转坐标。知道了是编辑器窗口拿这个窗口的偏移量做坐标转换。屏幕坐标减去窗口偏移得到窗口内坐标。再减标题栏得到内容区域坐标。第三步坐标 → 匹配区域。内容区域坐标x200判断落在编辑器窗口的哪个业务区域左边是素材面板中间是画布右边是属性栏。x200在中间画布区域。第四步区域 → 调处理器。找到了画布区域调对应的处理器insertCanvas()。这段代码解决什么问题完整路由链。文件router/TouchRouter.ets用途从触点坐标到业务处理的完整路由接入位置收到精准分享事件后classTouchRouter{privateregistry:WindowTargetRegistry;privateregionMatcher:RegionMatcher;privatedispatcher:BusinessDispatcher;constructor(registry:WindowTargetRegistry){this.registryregistry;this.regionMatchernewRegionMatcher();this.dispatchernewBusinessDispatcher();}// 完整路由触点 → 窗口 → 区域 → 业务routeTouch(screenX:number,screenY:number,payload:SharePayload){// 第一步找窗口letwindowthis.registry.findWindowAt(screenX,screenY);if(!window){console.warn(触点不在任何已注册窗口内);return;}console.info(命中窗口window.type);// 第二步坐标转换letcontentXscreenX-window.offsetX;letcontentYscreenY-window.offsetY-40;// 减标题栏// 第三步匹配业务区域letregionthis.regionMatcher.match(contentX,contentY);if(!region){console.warn(坐标不在任何业务区域内);return;}console.info(命中区域region.name);// 第四步分发到业务处理器this.dispatcher.dispatch(region.handler,payload);}}四、多窗口下最容易踩的坑多窗口比单窗口复杂多了坑也更多第一个坑窗口层级变化。两个窗口重叠了上面那个盖住下面那个。你碰到的是上面那个窗口但注册表里还把它算进去了。怎么判断哪个窗口在最前面要问窗口系统拿窗口层级顺序。第二个坑窗口移动后路由残留。窗口从左边拖到右边了但注册表还存着旧位置。用户碰新位置找不到窗口碰旧位置那个地方已经没有窗口了。所以窗口移动事件一定要监听。第三个坑窗口关闭后残留路由。预览窗口关了但注册表里没删掉。下次碰那个位置还去找这个已经不存在的窗口报错。窗口关闭时一定要注销。第四个坑悬浮窗覆盖。有个小的悬浮窗盖在主窗口上面用户碰的是悬浮窗但你的路由判断成了主窗口。悬浮窗也算一个窗口要纳入注册表。第五个坑平板和PC的窗口模型不一样。平板上可能就是全屏单窗口PC上可以多窗口自由拖。你的代码不能写死要适配不同设备形态。第六个坑自由窗口尺寸变化。用户拖窗口边缘调大小窗口尺寸变了区域边界也要跟着重算。不能写死250px、500px这种固定值。第三篇总结多窗口的核心问题不是坐标怎么算而是先知道是哪个窗口。单窗口模型里你默认只有一个应用窗口所以直接算坐标就行。多窗口下你得先回答这是谁的地盘再谈坐标转换。工程上最容易翻车的是窗口生命周期创建时注册、移动时更新、关闭时注销——这三步少一步路由就是错的。下一篇我们换个方向一次碰过来20张图怎么保证传输不乱

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑