碰一碰跨設(shè)備協(xié)作開發(fā)實(shí)戰(zhàn)》02:觸點(diǎn)坐標(biāo)怎么變成應(yīng)用里的精準(zhǔn)落點(diǎn)【鴻蒙心跡】)
上一篇我們解決了能傳這一篇要解決傳哪。同樣一張圖片碰左邊進(jìn)素材庫碰中間進(jìn)畫布碰右邊進(jìn)屬性區(qū)——坐標(biāo)到底是怎么變成業(yè)務(wù)落點(diǎn)的先講個(gè)我調(diào)試時(shí)遇到的事。我把CrossDrop工作臺(tái)分成了三個(gè)區(qū)域左邊素材庫中間編輯畫布右邊屬性面板。代碼寫好以后拿手機(jī)一碰——嘿不管碰哪圖片全進(jìn)了素材庫。我當(dāng)時(shí)就懵了。系統(tǒng)明明給了觸點(diǎn)坐標(biāo)為什么區(qū)域判斷全錯(cuò)后來打日志才發(fā)現(xiàn)系統(tǒng)給的坐標(biāo)是屏幕坐標(biāo)我拿它直接和組件坐標(biāo)比差了整整一個(gè)標(biāo)題欄的高度。就這一個(gè)坑調(diào)了一下午。一、先搞清楚四種坐標(biāo)根本不是一回事我們?nèi)粘Uf坐標(biāo)好像就是一個(gè)(x,y)。但在窗口系統(tǒng)里至少有四種坐標(biāo)每?jī)煞N之間都差著偏移量第一種——屏幕坐標(biāo)。整個(gè)顯示器左上角是(0,0)你碰屏幕哪一點(diǎn)系統(tǒng)返回的就是這個(gè)坐標(biāo)。這個(gè)坐標(biāo)是全局的不管你開了幾個(gè)窗口它都是相對(duì)于整個(gè)屏幕算的。第二種——窗口坐標(biāo)。相對(duì)于當(dāng)前窗口的左上角。窗口標(biāo)題欄、邊框這些算進(jìn)去沒有不同系統(tǒng)不一樣。窗口在屏幕上移了這個(gè)坐標(biāo)會(huì)不會(huì)變不會(huì)它是窗口內(nèi)部的。第三種——頁面坐標(biāo)。相對(duì)于應(yīng)用內(nèi)容區(qū)域。狀態(tài)欄、標(biāo)題欄、導(dǎo)航欄這些都不算從真正的內(nèi)容區(qū)域開始算。第四種——組件局部坐標(biāo)。相對(duì)于某個(gè)具體組件的左上角。素材庫有自己的坐標(biāo)畫布有自己的坐標(biāo)。你看從屏幕上哪一點(diǎn)到哪個(gè)組件的哪一塊中間要換算四層。少算任何一層結(jié)果都是錯(cuò)的。二、一步一步拆觸點(diǎn)坐標(biāo)怎么變成業(yè)務(wù)落點(diǎn)我們拿一個(gè)具體例子走一遍。假設(shè)用戶碰屏幕位置是(x300, y200)。第一步屏幕坐標(biāo) → 窗口坐標(biāo)先知道這個(gè)觸點(diǎn)落在哪個(gè)窗口里。系統(tǒng)會(huì)告訴你這個(gè)點(diǎn)屬于CrossDrop主窗口。窗口在屏幕上的位置是(offsetX100, offsetY50)。那窗口內(nèi)部坐標(biāo)就是窗口內(nèi)x 300 - 100 200窗口內(nèi)y 200 - 50 150這一步你得問窗口要它的位置。窗口移了位置變了換算公式就要跟著變。第二步窗口坐標(biāo) → 頁面坐標(biāo)窗口里不是全是內(nèi)容。上面有標(biāo)題欄左邊有側(cè)邊欄。標(biāo)題欄高度是40px。那內(nèi)容區(qū)域的起點(diǎn)就是y40。頁面坐標(biāo)頁面x 200假設(shè)左邊沒有邊距頁面y 150 - 40 110這一步最容易漏。我第一次調(diào)的時(shí)候就忘了減標(biāo)題欄高度結(jié)果所有區(qū)域判斷都往下偏了一截。第三步頁面坐標(biāo) → 組件區(qū)域判斷現(xiàn)在知道了內(nèi)容區(qū)域里的位置接下來判斷落在哪個(gè)業(yè)務(wù)區(qū)域。CrossDrop工作臺(tái)布局大概是這樣左邊素材庫x從0到250中間畫布x從250到750右邊屬性區(qū)x從750到1000頁面x200小于250那就是落在素材庫區(qū)域。碰中間畫布位置x大概是500落在250~750之間那就是插入畫布。碰右邊屬性區(qū)x大概是800大于750那就是進(jìn)入待處理資源區(qū)。這段代碼解決什么問題坐標(biāo)轉(zhuǎn)換工具。文件coordinate/CoordinateConverter.ets用途系統(tǒng)坐標(biāo)轉(zhuǎn)應(yīng)用業(yè)務(wù)坐標(biāo)接入位置觸點(diǎn)事件回調(diào)后classCoordinateConverter{// 窗口在屏幕上的偏移privatewindowOffsetX:number0;privatewindowOffsetY:number0;// 標(biāo)題欄高度privatetitleBarHeight:number40;// 更新窗口位置updateWindowPosition(offsetX:number,offsetY:number){this.windowOffsetXoffsetX;this.windowOffsetYoffsetY;}// 屏幕坐標(biāo) → 頁面內(nèi)容坐標(biāo)toContentCoordinate(screenX:number,screenY:number):{x:number;y:number}{return{x:screenX-this.windowOffsetX,y:screenY-this.windowOffsetY-this.titleBarHeight};}}第四步組件區(qū)域匹配光知道坐標(biāo)落在哪個(gè)大塊還不夠還要知道具體是哪個(gè)組件。這時(shí)候有兩種做法做法一自己算邊界。每個(gè)區(qū)域記錄自己的位置和尺寸判斷坐標(biāo)在不在這個(gè)矩形里。做法二用系統(tǒng)的HitTest。讓ArkUI自己做命中測(cè)試直接返回落在哪個(gè)組件上。自己算的好處是可控壞處是布局變了邊界也要跟著改。HitTest的好處是自動(dòng)跟隨布局壞處是有時(shí)候你想要的業(yè)務(wù)區(qū)域和組件邊界不完全一致。這段代碼解決什么問題區(qū)域匹配判斷。文件router/RegionMatcher.ets用途根據(jù)坐標(biāo)判斷業(yè)務(wù)落點(diǎn)接入位置坐標(biāo)轉(zhuǎn)換完成后interfaceRegion{id:string;name:string;x:number;y:number;width:number;height:number;handler:string;}classRegionMatcher{privateregions:Region[][];addRegion(region:Region){this.regions.push(region);}match(x:number,y:number):Region|null{for(letregionofthis.regions){if(xregion.xxregion.xregion.widthyregion.yyregion.yregion.height){returnregion;}}returnnull;}}// 使用constmatchernewRegionMatcher();matcher.addRegion({id:material,name:素材庫,x:0,y:0,width:250,height:800,handler:add_material});matcher.addRegion({id:canvas,name:編輯畫布,x:250,y:0,width:500,height:800,handler:insert_canvas});matcher.addRegion({id:property,name:屬性區(qū),x:750,y:0,width:250,height:800,handler:add_pending});三、坐標(biāo)這塊最容易踩的坑坐標(biāo)轉(zhuǎn)換看起來簡(jiǎn)單實(shí)際全是細(xì)節(jié)。我踩過的坑列一下第一個(gè)坑狀態(tài)欄高度沒算。系統(tǒng)返回的坐標(biāo)到底算不算狀態(tài)欄不同設(shè)備不一樣。手機(jī)和PC不一樣橫屏和豎屏也不一樣。這個(gè)要實(shí)測(cè)別想當(dāng)然。第二個(gè)坑窗口標(biāo)題欄偏移。PC上窗口有標(biāo)題欄你是從窗口邊框開始算還是從客戶區(qū)開始算這個(gè)差幾個(gè)像素判斷就錯(cuò)了。第三個(gè)坑縮放比例。有些設(shè)備有屏幕縮放比如125%縮放。你拿到的坐標(biāo)是物理像素還是邏輯像素這個(gè)一定要搞清楚。第四個(gè)坑窗口移動(dòng)了。用戶把窗口從左邊拖到右邊你還在用舊的窗口偏移量算結(jié)果全錯(cuò)。所以窗口位置變化的時(shí)候要監(jiān)聽并更新。第五個(gè)坑橫豎屏切換。手機(jī)豎屏和橫屏窗口尺寸完全變了。區(qū)域邊界也要跟著重算。第六個(gè)坑自由窗口尺寸變了。PC上用戶可以拖窗口邊緣調(diào)整大小窗口尺寸變了你的區(qū)域邊界也要重新布局。第二篇總結(jié)坐標(biāo)轉(zhuǎn)換這件事核心不是寫幾行換算代碼而是搞清楚四種坐標(biāo)之間的層級(jí)關(guān)系屏幕坐標(biāo) → 窗口坐標(biāo) → 頁面坐標(biāo) → 組件局部坐標(biāo)。每一層都有自己的偏移量少算一層結(jié)果就錯(cuò)。工程上最容易翻車的不是算法是細(xì)節(jié)狀態(tài)欄高度、標(biāo)題欄偏移、縮放比例、窗口移動(dòng)、橫豎屏切換——這些環(huán)境變量任何一個(gè)變了你的坐標(biāo)換算就要跟著變。下一篇我們把難度再往上提一級(jí)PC上同時(shí)開了三個(gè)窗口用戶碰屏幕的時(shí)候怎么知道他碰的是哪個(gè)窗口