實(shí)戰(zhàn):從原理到布局適配全攻略)
開頭我最早接觸HarmonyOS ArkUI窗口旋轉(zhuǎn)是被一個(gè)蠻尷尬的場(chǎng)景逼著去研究的應(yīng)用只是從豎屏轉(zhuǎn)了一下橫屏結(jié)果頁面直接“重排”得面目全非底部按鈕跑到了屏幕中間列表的滾動(dòng)位置也丟了用戶切回來之后直呼“手機(jī)是不是壞了”。后來認(rèn)真查了一圈才發(fā)現(xiàn)窗口旋轉(zhuǎn)這個(gè)功能在ArkUI里不只是“屏幕轉(zhuǎn)一下”那么簡(jiǎn)單它牽扯到配置文件里的方向聲明、窗口事件回調(diào)、布局的重新計(jì)算還有應(yīng)用狀態(tài)怎么在旋轉(zhuǎn)前后保住。當(dāng)時(shí)網(wǎng)上能找到的資料大多是零散的接口截圖沒有一篇能讓我從“完全不懂”到“能落地跑通”的文章。這篇文章就按我自己的學(xué)習(xí)路徑來寫先把旋轉(zhuǎn)背后的邏輯講清楚再給你一套可以直接抄的配置和代碼最后把踩過的坑和調(diào)試心得都倒出來。適合剛接觸ArkUI的開發(fā)者也適合那些已經(jīng)寫了幾個(gè)頁面、但一直沒搞明白旋轉(zhuǎn)到底該怎么處理的開發(fā)者。1. 旋轉(zhuǎn)不是“轉(zhuǎn)一下屏幕”那么簡(jiǎn)單先理解ArkUI窗口旋轉(zhuǎn)的運(yùn)作層級(jí)很多人一開始都會(huì)把“窗口旋轉(zhuǎn)”理解成“讓屏幕方向變一下”其實(shí)真實(shí)情況要復(fù)雜一截。在HarmonyOS這套體系里旋轉(zhuǎn)至少涉及三個(gè)層級(jí)系統(tǒng)設(shè)置、應(yīng)用配置、頁面布局。系統(tǒng)設(shè)置決定設(shè)備允不允許旋轉(zhuǎn)比如某些折疊屏和平板會(huì)有“自動(dòng)旋轉(zhuǎn)”開關(guān)應(yīng)用配置決定你的應(yīng)用在什么情況下響應(yīng)旋轉(zhuǎn)是在module.json5里聲明方向還是讓應(yīng)用跟隨傳感器頁面布局則決定旋轉(zhuǎn)之后UI怎么重新排布。這三個(gè)層級(jí)只要有一層沒配合好旋轉(zhuǎn)起來就會(huì)出現(xiàn)我們常說的“布局崩了”“狀態(tài)丟了”“動(dòng)畫卡頓”這類問題。ArkUI和傳統(tǒng)的移動(dòng)端開發(fā)有個(gè)很不一樣的地方它更多是聲明式的界面長(zhǎng)什么樣由你描述的UI狀態(tài)決定而不是一幀一幀手工去改。窗口旋轉(zhuǎn)本質(zhì)上是窗口尺寸發(fā)生了變化——豎屏變成橫屏寬高比例交換——而ArkUI拿到新的窗口尺寸后會(huì)重新觸發(fā)布局計(jì)算、刷新UI。所以你要做的并不是“在旋轉(zhuǎn)發(fā)生的那一刻手動(dòng)搬控件”而是把布局規(guī)則寫得足夠健壯讓它在任意寬高比下都能合理排布。這里還要提一下Stage模型和Ability的關(guān)系。在HarmonyOS里窗口是由UIAbility來承載的窗口的方向?qū)傩跃驮赨IAbility的配置里。也就是說你希望某個(gè)頁面橫屏、某個(gè)頁面豎屏一般要拆成不同的Ability去分別聲明而不是在同一個(gè)頁面內(nèi)部靠代碼生硬切換。我在前期就吃過這個(gè)虧想在同一個(gè)Ability里針對(duì)不同子頁面改方向結(jié)果發(fā)現(xiàn)窗口方向一旦被設(shè)置整條Ability鏈路上的頁面都會(huì)受影響。理解了這三個(gè)層級(jí)后面所有配置和代碼才有落腳點(diǎn)。1.1 ArkUI窗口旋轉(zhuǎn)的設(shè)計(jì)邊界ArkUI在窗口旋轉(zhuǎn)這件事上能力邊界大致是這幾條讀取當(dāng)前窗口方向、監(jiān)聽窗口方向或尺寸變化、主動(dòng)設(shè)置窗口方向、控制是否跟隨系統(tǒng)旋轉(zhuǎn)。日常項(xiàng)目里用到的基本就是這些。但你要留意ArkUI的“窗口旋轉(zhuǎn)”和“頁面內(nèi)某個(gè)組件旋轉(zhuǎn)”是兩碼事。組件旋轉(zhuǎn)用rotation屬性是純視覺上的變化窗口旋轉(zhuǎn)則是整個(gè)應(yīng)用界面的坐標(biāo)系和布局參照都變了。如果混在一起想容易把實(shí)現(xiàn)方案搞亂。還有一點(diǎn)旋轉(zhuǎn)觸發(fā)時(shí)系統(tǒng)會(huì)有一定的動(dòng)畫過渡過程窗口尺寸并不是瞬間切換的。如果你的代碼在旋轉(zhuǎn)回調(diào)里做了比較重的操作比如重新拉起網(wǎng)絡(luò)請(qǐng)求、重新構(gòu)建大列表用戶會(huì)明顯感覺到卡頓或者白屏閃爍。所以旋轉(zhuǎn)回調(diào)里做“輕量更新”和“狀態(tài)保存”是基本原則。1.2 旋轉(zhuǎn)相關(guān)的核心概念方向、尺寸、密度要聊窗口旋轉(zhuǎn)有三個(gè)概念必須拎出來對(duì)齊一下方向orientation、尺寸size、密度density。方向就是屏幕當(dāng)前的朝向通常有豎屏、橫屏、反向豎屏、反向橫屏這些狀態(tài)。尺寸比較好理解旋轉(zhuǎn)會(huì)導(dǎo)致寬高互換但要注意在ArkUI里尺寸單位是vp虛擬像素它和物理像素之間隔著密度換算。密度這個(gè)變量在某些場(chǎng)景會(huì)坑你比如不同設(shè)備的默認(rèn)密度不一樣同樣的寬高布局在不同設(shè)備上旋轉(zhuǎn)后的表現(xiàn)也會(huì)有差異。實(shí)操中我一般建議把布局盡可能用相對(duì)單位和彈性布局去寫避免寫死寬高。這樣做的好處是旋轉(zhuǎn)發(fā)生時(shí)窗口尺寸變了布局能自動(dòng)適應(yīng)不需要你在代碼里針對(duì)每個(gè)尺寸都寫一套分支判斷。2. 從零跑通最簡(jiǎn)窗口旋轉(zhuǎn)方向配置與兩種主流控制方式最基礎(chǔ)的窗口旋轉(zhuǎn)實(shí)現(xiàn)其實(shí)不需要寫一行JavaScript/ArkTS代碼。HarmonyOS的Stage模型允許你直接在module.json5里聲明Ability的方向?qū)傩浴O瓤匆粋€(gè)最簡(jiǎn)配置。2.1 在module.json5中聲明窗口方向項(xiàng)目里找到entry/src/main/module.json5在abilities數(shù)組對(duì)應(yīng)的Ability節(jié)點(diǎn)下添加orientation字段{ module: { abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, orientation: autoRotation, exported: true, skills: [ { entities: [entity.system.home], actions: [action.system.home] } ] } ] } }orientation支持的值在文檔里能查到我實(shí)際用下來比較常用的有這么幾個(gè)取值含義適用場(chǎng)景portrait鎖定豎屏閱讀類、列表類應(yīng)用landscape鎖定橫屏視頻、游戲類應(yīng)用sensorLandscape跟隨傳感器但只在橫屏方向間切換橫屏為主的工具類應(yīng)用sensorPortrait跟隨傳感器但只在豎屏方向間切換豎屏為主的工具類應(yīng)用autoRotation跟隨系統(tǒng)自動(dòng)旋轉(zhuǎn)大多數(shù)普通應(yīng)用需要注意autoRotation和unspecified并不完全一樣。unspecified更多是“交給系統(tǒng)決定”而autoRotation會(huì)跟隨用戶的系統(tǒng)旋轉(zhuǎn)開關(guān)。如果你希望應(yīng)用默認(rèn)跟隨用戶設(shè)置就用autoRotation。2.2 代碼動(dòng)態(tài)控制窗口方向聲明式配置適合“整個(gè)Ability統(tǒng)一方向”。但如果你的應(yīng)用需要在運(yùn)行過程中動(dòng)態(tài)切換方向比如用戶點(diǎn)了按鈕進(jìn)入全屏播放視頻那就得用代碼控制。在UIAbility的onWindowStageCreate階段拿窗口對(duì)象然后設(shè)置首選方向import { window } from kit.ArkUI; export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { const mainWindow windowStage.getMainWindowSync(); // 讓窗口跟隨傳感器自動(dòng)旋轉(zhuǎn) mainWindow.setPreferredOrientation(autoRotation, (err) { if (err) { console.error(setPreferredOrientation failed, code is ${err.code}); } }); } }這里有個(gè)細(xì)節(jié)setPreferredOrientation是異步接口第二個(gè)參數(shù)是回調(diào)。實(shí)際產(chǎn)品里如果方向切換后有埋點(diǎn)需求可以在回調(diào)里做統(tǒng)計(jì)。另外這個(gè)設(shè)置是“偏好”性質(zhì)的極端情況下系統(tǒng)可能會(huì)拒絕比如某些特殊窗口模式下。2.3 動(dòng)態(tài)鎖定方向的策略視頻播放類應(yīng)用常用的一個(gè)交互是進(jìn)去時(shí)橫屏退出時(shí)恢復(fù)原來的方向。我處理這種需求時(shí)習(xí)慣先把當(dāng)前方向存起來再設(shè)置目標(biāo)方向import { window } from kit.ArkUI; class OrientationManager { private currentOrientation: string portrait; saveOrientation(windowObj: window.Window) { this.currentOrientation windowObj.getPreferredOrientation(); } lockLandscape(windowObj: window.Window) { // 橫屏鎖定兩個(gè)方向都支持但以橫屏為主 this.saveOrientation(windowObj); windowObj.setPreferredOrientation(sensorLandscape); } restoreOrientation(windowObj: window.Window) { windowObj.setPreferredOrientation(this.currentOrientation); } }這樣處理的好處是用戶之前如果是豎屏鎖定狀態(tài)退出全屏后能恢復(fù)豎屏如果之前是自動(dòng)旋轉(zhuǎn)退出后也能恢復(fù)自動(dòng)旋轉(zhuǎn)不會(huì)出現(xiàn)“退出全屏后發(fā)現(xiàn)應(yīng)用再也不轉(zhuǎn)了”的問題。3. 旋轉(zhuǎn)發(fā)生那一刻應(yīng)用在干什么生命周期回調(diào)與窗口事件監(jiān)聽配置搞定之后才是真正考驗(yàn)工程能力的地方。前面說過窗口旋轉(zhuǎn)會(huì)導(dǎo)致窗口尺寸變化、觸發(fā)頁面重新布局。但很多業(yè)務(wù)場(chǎng)景需要在旋轉(zhuǎn)的那一刻做點(diǎn)事保存表單數(shù)據(jù)、暫停視頻、更新圖表、收起鍵盤等。要做到這些你得搞清楚應(yīng)用在旋轉(zhuǎn)時(shí)經(jīng)歷了什么。3.1 ConfigurationUpdate回調(diào)是首選入口在Stage模型中UIAbility提供了onConfigurationUpdate回調(diào)。當(dāng)系統(tǒng)配置發(fā)生變化包括方向變化、語言切換、深淺色切換時(shí)系統(tǒng)都會(huì)觸發(fā)這個(gè)回調(diào)。import { AbilityConstant, ConfigurationConstant, UIAbility } from kit.AbilityKit; import { common } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onConfigurationUpdate(newConfig: common.Configuration): void { if (newConfig.direction ConfigurationConstant.Direction.DIRECTION_HORIZONTAL) { console.info(窗口方向切換到橫屏); // 這里可以通知頁面組件做布局優(yōu)化比如切換列表樣式 } else if (newConfig.direction ConfigurationConstant.Direction.DIRECTION_VERTICAL) { console.info(窗口方向切換到豎屏); // 做一些豎屏特有的UI調(diào)整 } } }需要提醒一下onConfigurationUpdate回調(diào)只是“通知”你這個(gè)事發(fā)生了它不保證窗口尺寸已經(jīng)切換完成。如果你要在回調(diào)里讀取窗口的最新寬高最好在下一幀或者等窗口事件到了再取。3.2 監(jiān)聽窗口的尺寸與方向變化事件比onConfigurationUpdate更精確的是窗口對(duì)象自帶的尺寸變化監(jiān)聽。在ArkUI里Window對(duì)象支持windowSizeChange事件。豎屏轉(zhuǎn)橫屏本質(zhì)上是窗口寬度和高度交換所以監(jiān)聽windowSizeChange也能感知旋轉(zhuǎn)而且能拿到實(shí)時(shí)的寬高數(shù)值。import { window } from kit.ArkUI; function listenWindowSizeChange(windowObj: window.Window) { windowObj.on(windowSizeChange, (newSize: window.Size) { console.info(新的窗口尺寸width${newSize.width}, height${newSize.height}); // 判斷寬高比寬大于高視為橫屏 const isLandscape newSize.width newSize.height; }); }不過我自己用下來寬度大于高度這個(gè)判斷方式在折疊屏上會(huì)有一點(diǎn)意外因?yàn)檎郫B屏的展開態(tài)可能會(huì)有一個(gè)接近方形的中間狀態(tài)。如果你的應(yīng)用要兼容折疊屏建議不要只用“寬大于高”這單一條件判斷橫豎屏還要結(jié)合密度和設(shè)備的折疊狀態(tài)來看。3.3 應(yīng)用退到后臺(tái)再轉(zhuǎn)屏的坑還有一種情況容易被忽略用戶先按Home鍵把應(yīng)用退到后臺(tái)然后在系統(tǒng)設(shè)置里改了方向再切回來。這時(shí)候應(yīng)用可能不會(huì)走完整的旋轉(zhuǎn)回調(diào)鏈路但UI實(shí)際上已經(jīng)不一樣了。所以在onForeground恢復(fù)的時(shí)候最好重新讀取一次當(dāng)前窗口方向并主動(dòng)刷新需要方向信息的UI部分。我遇到過的問題是彈窗類的浮層在后臺(tái)被旋轉(zhuǎn)后回到前臺(tái)時(shí)還保留著舊的布局參數(shù)結(jié)果位置歪了。后來在onForeground里重新計(jì)算浮層位置才治好。這個(gè)經(jīng)驗(yàn)可能冷門但遇到類似浮層、彈窗問題的同學(xué)應(yīng)該深有體會(huì)。4. 橫豎屏布局適配的實(shí)用策略別只想著寫兩套UI旋轉(zhuǎn)的代碼邏輯其實(shí)簡(jiǎn)單真正讓開發(fā)者頭疼的是布局適配。很多新手的思路是“橫屏一套、豎屏一套”寫兩個(gè)界面然后根據(jù)方向切換。這在簡(jiǎn)單頁面下能跑但頁面一多、組件一復(fù)雜兩套UI的維護(hù)成本會(huì)迅速膨脹。ArkUI的思路更傾向于“一套布局自適應(yīng)變化”。下面幾個(gè)策略是我在實(shí)際項(xiàng)目里驗(yàn)證過比較有效的。4.1 優(yōu)先使用彈性布局與柵格布局ArkUI提供了一系列布局容器Row、Column、Flex、GridRow等。你要做的就是把布局寫得“能伸縮”而不是寫死。比如一個(gè)列表頁豎屏?xí)r占滿整寬橫屏?xí)r通過GridRow把內(nèi)容區(qū)限制在中間固定寬度就是一種很自然的適配。Entry Component struct GridRowDemo { build() { GridRow({ columns: { sm: 4, md: 8, lg: 12 }, gutter: { x: 8, y: 8 } }) { GridCol({ span: { sm: 4, md: 4, lg: 6 } }) { Text(主內(nèi)容區(qū)域) .width(100%) .height(200) .backgroundColor(0xeeeeee) } GridCol({ span: { sm: 4, md: 4, lg: 6 } }) { Text(側(cè)邊信息區(qū)域) .width(100%) .height(200) .backgroundColor(0xcccccc) } } .width(100%) } }上面這個(gè)GridRow示例小屏sm下一行4列、兩個(gè)子項(xiàng)各占4列相當(dāng)于堆疊大屏lg下兩個(gè)子項(xiàng)各占6列正好并排。這樣旋轉(zhuǎn)前后你根本不用寫方向判斷系統(tǒng)會(huì)根據(jù)窗口尺寸自動(dòng)選列數(shù)。4.2 資源限定詞適配橫屏資源如果某些資源確實(shí)需要區(qū)分橫豎屏——比如啟動(dòng)圖、宣傳圖、背景圖——HarmonyOS的資源限定詞可以幫你按方向加載對(duì)應(yīng)資源。在resources/base/element、resources/base/media同級(jí)目錄下可以創(chuàng)建帶方向限定詞的資源目錄比如resources/base/media/默認(rèn)資源resources/landscape/media/橫屏資源resources/portrait/media/豎屏資源這樣同一資源名在不同方向下會(huì)自動(dòng)加載不同的文件。我用這個(gè)特性處理過橫屏啟動(dòng)頁的適配比在代碼里判斷方向再切圖干凈得多。4.3 善用屬性變化綁定減少方向分支代碼ArkUI的LayoutWeight、aspectRatio等屬性也能顯著減少方向分支。比如視頻播放器的容器我希望橫屏?xí)r鋪滿全屏、豎屏?xí)r保持16:9用aspectRatio就可以很優(yōu)雅地實(shí)現(xiàn)VideoPlayerContainer() { // 內(nèi)容實(shí)現(xiàn) } .aspectRatio(this.isLandscape ? 1.0 : 16 / 9) .width(100%)這里isLandscape可以取自窗口尺寸或配置更新回調(diào)。比起寫兩套布局這種“改幾個(gè)屬性值”的做法維護(hù)起來輕松太多。5. 避坑筆記方向鎖定、應(yīng)用內(nèi)旋轉(zhuǎn)與窗口尺寸的糾纏做窗口旋轉(zhuǎn)期間我踩過的坑能湊一籮筐。挑幾個(gè)典型的說一說每一個(gè)都是我花了不少時(shí)間才定位到原因的。5.1 方向鎖定后應(yīng)用內(nèi)頁面無法單獨(dú)旋轉(zhuǎn)這個(gè)坑出現(xiàn)得很隱蔽某個(gè)頁面不需要旋轉(zhuǎn)但另一個(gè)頁面需要。我一開始直接在需要旋轉(zhuǎn)的頁面里調(diào)用setPreferredOrientation(landscape)結(jié)果發(fā)現(xiàn)整條Ability的窗口都帶過去了后面無論怎么跳轉(zhuǎn)窗口都保持橫屏。后來才理解方向是窗口級(jí)別的屬性不是頁面級(jí)別的屬性。如果你想做“應(yīng)用大部分頁面豎屏、視頻頁橫屏”要么把視頻頁拆成單獨(dú)的Ability并提前在module.json5里聲明橫屏要么在頁面進(jìn)入和退出時(shí)用代碼主動(dòng)切方向并且保證退出時(shí)恢復(fù)。沒有哪種方案能做到“頁面A聲明豎屏、頁面B聲明橫屏”而不影響彼此。5.2 setPreferredOrientation的回調(diào)時(shí)機(jī)不能用來做UI重構(gòu)setPreferredOrientation的回調(diào)只表示“設(shè)置命令已經(jīng)執(zhí)行”不代表窗口尺寸已經(jīng)變成目標(biāo)尺寸。如果你在回調(diào)里立刻讀取窗口寬度并修改布局大概率拿到的是舊值。正確的做法是結(jié)合windowSizeChange事件在事件回調(diào)里讀取最新的Size再做UI重構(gòu)。這兩者之間大概差一個(gè)動(dòng)畫過渡周期雖然時(shí)間不長(zhǎng)但對(duì)用戶體驗(yàn)和邏輯正確性影響不小。5.3 鍵盤彈起時(shí)轉(zhuǎn)屏導(dǎo)致的布局錯(cuò)亂鍵盤和旋轉(zhuǎn)同時(shí)發(fā)生時(shí)布局錯(cuò)亂概率非常大。豎屏狀態(tài)下輸入框彈出鍵盤這時(shí)候轉(zhuǎn)橫屏窗口可用高度劇烈變化鍵盤狀態(tài)可能導(dǎo)致輸入框被遮擋或者動(dòng)畫卡頓。我的處理方案是轉(zhuǎn)屏前主動(dòng)收起鍵盤或者延遲一小段時(shí)間再做滾動(dòng)定位。有些場(chǎng)景下還需要重算keyboardAvoid模式。這里沒有萬能公式只能靠多測(cè)不同輸入場(chǎng)景。5.4 傳感器旋轉(zhuǎn)與應(yīng)用內(nèi)旋轉(zhuǎn)的沖突autoRotation是跟隨系統(tǒng)傳感器的而代碼里setPreferredOrientation(landscape)是應(yīng)用層設(shè)置。兩者沖突時(shí)以最近的一次設(shè)置為準(zhǔn)。如果你先設(shè)置了autoRotation又立刻設(shè)置landscape可能得到的結(jié)果是“看起來鎖定了但傳感器偶爾能轉(zhuǎn)過來然后被拉回去”。避免這種抖動(dòng)的方法很簡(jiǎn)單不要同時(shí)混用這兩種方式。要有明確的狀態(tài)管理比如用一個(gè)currentOrientationMode變量統(tǒng)一控制所有方向設(shè)置的入口避免多處代碼各自調(diào)用設(shè)置接口。5.5 折疊屏和平板的旋轉(zhuǎn)差異折疊屏設(shè)備上“展開”不一定會(huì)觸發(fā)旋轉(zhuǎn)。有些設(shè)備展開后是從豎屏變橫屏有些是保持豎屏但寬度變大。這就導(dǎo)致一個(gè)問題你用方向來判斷頁面上的布局在折疊屏上完全不可靠。更好的判斷標(biāo)準(zhǔn)是窗口寬高比和可用寬度而不是方向枚舉值。平板的情況更復(fù)雜因?yàn)槠桨鍣M向使用比例很高而且用戶對(duì)“橫屏”的預(yù)期跟手機(jī)上完全不同。如果應(yīng)用要做平板適配建議單獨(dú)走一套大屏布局規(guī)范別只指望旋轉(zhuǎn)適配邏輯。6. 實(shí)際調(diào)試經(jīng)驗(yàn)真機(jī)、模擬器與不同形態(tài)設(shè)備的旋轉(zhuǎn)差異代碼寫完了能不能跑好看還得看調(diào)試。我調(diào)試旋轉(zhuǎn)功能的設(shè)備組合一般是“手機(jī)真機(jī) 模擬器 平板/折疊屏”因?yàn)檫@三類設(shè)備的旋轉(zhuǎn)行為差異真的很大。6.1 模擬器和真機(jī)的差異模擬器上旋轉(zhuǎn)通常非?!耙?guī)矩”方向一切就切幾乎沒有中間過渡真機(jī)上則帶有系統(tǒng)動(dòng)畫、密度換算、劉海區(qū)避讓等額外處理。所以如果你在模擬器上發(fā)現(xiàn)布局沒問題不能直接下“適配完成”的結(jié)論務(wù)必在真機(jī)上再走一遍。還有一個(gè)很實(shí)際的問題模擬器對(duì)傳感器的模擬并不可靠。你設(shè)置autoRotation后模擬器上的表現(xiàn)未必能還原真機(jī)的翻轉(zhuǎn)效果。我自己習(xí)慣用“方向快捷鍵”來測(cè)試但最終驗(yàn)收一定在真機(jī)。6.2 檢查旋轉(zhuǎn)相關(guān)日志與狀態(tài)調(diào)試旋轉(zhuǎn)問題時(shí)日志非常有用。我一般會(huì)做這幾件事在onConfigurationUpdate、windowSizeChange回調(diào)里打日志確認(rèn)回調(diào)順序和數(shù)值。在布局刷新前后打印窗口尺寸和關(guān)鍵組件的寬高對(duì)比是否是預(yù)期值。如果出現(xiàn)旋轉(zhuǎn)后組件位置偏移優(yōu)先檢查父容器的對(duì)齊方式其次是檢查有沒有寫死尺寸。如果回調(diào)根本不打日志那問題可能出在上游比如配置沒生效、窗口對(duì)象沒綁定成功先排查這些基礎(chǔ)項(xiàng)。6.3 一個(gè)自查清單最后分享一個(gè)我每次發(fā)布前都會(huì)過一遍的旋轉(zhuǎn)自查清單。別嫌簡(jiǎn)單真能把這幾項(xiàng)都做對(duì)旋轉(zhuǎn)相關(guān)的線上問題至少能少掉七八成檢查項(xiàng)說明方向配置符合產(chǎn)品預(yù)期豎屏類應(yīng)用不要漏配orientation導(dǎo)致意外橫屏旋轉(zhuǎn)回調(diào)里沒有耗時(shí)操作不在回調(diào)里發(fā)網(wǎng)絡(luò)請(qǐng)求、不做大量列表刷新輸入框鍵盤狀態(tài)正常橫豎屏切換后輸入框不被遮擋浮層、彈窗位置正確旋轉(zhuǎn)后浮層重新計(jì)算位置不殘留舊布局折疊屏展開狀態(tài)下布局正常不能只按方向判斷要按寬度和寬高比判斷退出全屏后方向能恢復(fù)恢復(fù)用戶進(jìn)入前的方向狀態(tài)狀態(tài)數(shù)據(jù)不丟失列表滾動(dòng)位置、表單輸入內(nèi)容在旋轉(zhuǎn)后仍保留寫在最后的個(gè)人體會(huì)窗口旋轉(zhuǎn)這種東西看著不起眼做起來全是細(xì)節(jié)?;ㄒ粋€(gè)下午把配置和回調(diào)機(jī)制讀透遠(yuǎn)遠(yuǎn)比在頁面上寫各種魔法數(shù)字切來切去要值得多。我的習(xí)慣是每個(gè)新項(xiàng)目開工第一周就把旋轉(zhuǎn)適配的基礎(chǔ)能力搭好后面所有頁面都默認(rèn)具備這個(gè)能力。否則等項(xiàng)目里幾十個(gè)頁面寫完再回頭補(bǔ)那才叫真正的災(zāi)難。