1# 窗口管理开发常见问题 2<!--Kit: ArkUI--> 3<!--Subsystem: Window--> 4<!--Owner: @oh_wangxk; @logn--> 5<!--Designer: @hejunfei1991--> 6<!--Tester: @qinliwen0417--> 7<!--Adviser: @ge-yafang--> 8 9## 如何获取状态栏和导航栏高度(API 9) 10 11**解决措施** 12 13在加载窗口内容之前,采用systemAvoidAreaChange事件监听。 14 15**代码示例** 16 17``` 18// MainAbility.ts 19import window from '@ohos.window'; 20 21/** 22 * 设置沉浸式窗口,并获取状态栏和导航栏高度 23 * @param mainWindow 主窗口对象 24 */ 25async function enterImmersion(mainWindow: window.Window) { 26 window.on("systemBarTintChange", (data) => { 27 let avoidAreaRect = data.regionTint[0].region; //data.regionTint是个数组,包含状态栏、导航栏的矩形区域坐标。 28 }) 29 await mainWindow.setFullScreen(true) 30 await mainWindow.setSystemBarEnable(["status", "navigation"]) 31 await mainWindow.systemBarProperties({ 32 navigationBarColor: "#00000000", 33 statusBarColor: "#00000000", 34 navigationBarContentColor: "#FF0000", 35 statusBarContentColor: "#FF0000" 36 }) 37} 38export default class MainAbility extends Ability { 39 // do something 40 async onWindowStageCreate(windowStage: window.WindowStage) { 41 let mainWindow = await windowStage.getMainWindow() 42 await enterImmersion(mainWindow) 43 windowStage.loadContent('pages/index') 44 } 45 // do something 46} 47``` 48 49 50## 应用如何设置隐藏顶部的状态栏(API 9) 51 52**解决措施** 53 54在UIAbility的onWindowStageCreate的生命周期中设置setWindowSystemBarEnable接口即可。 55 56**代码示例** 57 58``` 59onWindowStageCreate(windowStage){ 60 windowStage.getMainWindowSync().setWindowSystemBarEnable([]) 61 ...... 62} 63``` 64 65**参考链接** 66 67[窗口基础能力文档](../reference/apis-arkui/arkts-apis-window-Window.md) 68 69## 如何锁定设备竖屏,使得窗口不随屏幕旋转(API 9) 70 71适用于Stage模型。 72 73**解决措施** 74 75采用窗口的setPreferredOrientation方法可以实现该效果,将orientation参数设置为window.Orientation.PORTRAIT时,可锁定屏幕为竖屏。 76 77**代码示例** 78 79``` 80import window from "@ohos.window"; 81//1.获取窗口实例对象,新建窗口使用createWindow方法,获取已有的窗口使用findWindow方法 82let windowClass = null; 83let config = {name: "alertWindow", windowType: window.WindowType.TYPE_SYSTEM_ALERT, ctx: this.context}; 84try { 85 let promise = window.createWindow(config); 86 promise.then((data)=> { 87 windowClass = data; 88 console.info('Succeeded in creating the window. Data:' + JSON.stringify(data)); 89 }).catch((err)=>{ 90 console.error('Failed to create the Window. Cause:' + JSON.stringify(err)); 91 });} catch (exception) { 92 console.error('Failed to create the window. Cause: ' + JSON.stringify(exception)); 93} 94//2.窗口实例使用setPreferredOrientation方法,设置窗口的显示方向,PORTRAIT为固定竖屏,其他方向可参照参考链接 95let orientation = window.Orientation.PORTRAIT; 96if (windowClass) { 97 windowClass.setPreferredOrientation(orientation, (err) => { 98 if (err.code) { 99 console.error('Failed to set window orientation. Cause: ' + JSON.stringify(err)); 100 return; 101 } 102 console.info('Succeeded in setting window orientation.'); 103} 104``` 105 106**参考链接** 107 108[window.Orientation](../reference/apis-arkui/arkts-apis-window-e.md#orientation9) 109 110## 调用Window实例的setWindowSystemBarProperties接口设置窗口状态栏和导航栏的高亮属性时不生效(API 9) 111 112适用于Stage模型。 113 114**解决措施** 115 116状态栏字体高亮属性的本质就只是让字体变成白色。调用window实例的setWindowSystemBarProperties接口时,如果设置了状态栏内容颜色statusBarContentColor,就以开发者设置的颜色为准,isStatusBarLightIcon状态栏字体高亮属性就不生效;同理,如果设置了导航栏内容颜色navigationBarContentColor,isNavigationBarLightIcon导航栏字体高亮属性就不生效。 117 118**参考链接** 119 120[window.SystemBarProperties](../reference/apis-arkui/arkts-apis-window-i.md#systembarproperties) 121 122 123## 如何保持屏幕常亮(API 9) 124 125**解决措施** 126 127设置屏幕常亮,不熄屏。 128 129获取窗口实例对象后,调用[setWindowKeepScreenOn方法](../reference/apis-arkui/arkts-apis-window-Window.md#setwindowkeepscreenon9)可设置屏幕是否常亮。 130 131**代码示例** 132 133``` 134let isKeepScreenOn = true; 135try { 136 windowClass.setWindowKeepScreenOn(isKeepScreenOn, (err) => { 137 if (err.code) { 138 console.error('Failed to set the screen to be always on. Cause: ' + JSON.stringify(err)); 139 return; 140 } 141 console.info('Succeeded in setting the screen to be always on.'); 142 }); 143} catch (exception) { 144 console.error('Failed to set the screen to be always on. Cause: ' + JSON.stringify(exception)); 145} 146``` 147 148 149## 如何监听窗口大小的变化(API 9) 150 151获取窗口实例对象后,可以通过[window.on('windowSizeChange')](../reference/apis-arkui/arkts-apis-window-Window.md#onwindowsizechange7)方法实现对窗口尺寸大小变化的监听。 152 153需要注意的是,在window侧如果窗口大小没发生变化,此监听不会被触发。如直接旋转180度的情况下,窗口大小并没有改变,此时不会通知回调。在这种情况下,应用可以通过监听[display.on('change')](../reference/apis-arkui/js-apis-display.md#displayonaddremovechange)事件,在callback中通过display接口来获取窗口尺寸大小。 154 155``` 156try { 157 windowClass.on('windowSizeChange', (data) => { 158 console.info('Succeeded in enabling the listener for window size changes. Data: ' + JSON.stringify(data)); 159 }); 160} catch (exception) { 161 console.error('Failed to enable the listener for window size changes. Cause: ' + JSON.stringify(exception)); 162} 163``` 164 165## 如何监听当前屏幕的横竖屏状态(API 10) 166 167**解决措施** 168 169应用可以通过display.on监听屏幕状态改变。 170 171**参考链接** 172 173[开启显示设备变化的监听](../reference/apis-arkui/js-apis-display.md#displayonaddremovechange) 174 175## 如何实现页面跟随屏幕横竖屏自动旋转(API 10) 176 177**解决措施** 178 1791.Abilty级别配置:在模块配置文件module.json5中将EntryAbility设置为"orientation"。 1802.动态设置:使用window.setPreferredOrientation设置窗口方向。 181 182**代码示例** 183```ts 184import window from '@ohos.window'; 185import display from '@ohos.display'; 186 187const TAG = 'foo' 188const ORIENTATION: Array<string> = ['垂直', '水平', '反向垂直', '反向水平'] 189 190@Entry 191@Component 192struct ScreenTest { 193 @State rotation: number = 0 194 @State message: string = ORIENTATION[this.rotation] 195 196 aboutToAppear() { 197 this.setOrientation() 198 199 let callback = async () => { 200 let d = display.getDefaultDisplaySync() 201 this.rotation = d.rotation 202 this.message = ORIENTATION[this.rotation] 203 console.info(TAG, JSON.stringify(d)) 204 } 205 try { 206 display.on("change", callback); // 监听屏幕状态改变 207 } catch (exception) { 208 console.error(TAG, 'Failed to register callback. Code: ' + JSON.stringify(exception)); 209 } 210 } 211 212 setOrientation() { 213 try { 214 window.getLastWindow(this.getUIContext().getHostContext(), (err, data) => { // 获取window实例 215 if (err.code) { 216 console.error(TAG, 'Failed to obtain the top window. Cause: ' + JSON.stringify(err)); 217 return; 218 } 219 let windowClass = data; 220 console.info(TAG, 'Succeeded in obtaining the top window. Data: ' + JSON.stringify(data)); 221 222 let orientation = window.Orientation.AUTO_ROTATION; // 设置窗口方向为传感器自动旋转模式。 223 try { 224 windowClass.setPreferredOrientation(orientation, (err) => { 225 if (err.code) { 226 console.error(TAG, 'Failed to set window orientation. Cause: ' + JSON.stringify(err)); 227 return; 228 } 229 console.info(TAG, 'Succeeded in setting window orientation.'); 230 }); 231 } catch (exception) { 232 console.error(TAG, 'Failed to set window orientation. Cause: ' + JSON.stringify(exception)); 233 } 234 ; 235 }); 236 } catch (exception) { 237 console.error(TAG, 'Failed to obtain the top window. Cause: ' + JSON.stringify(exception)); 238 } 239 ; 240 } 241 242 build() { 243 Row() { 244 Column() { 245 Text(`${this.rotation}`).fontSize(25) 246 Text(`${this.message}`).fontSize(25) 247 } 248 .width("100%") 249 } 250 .height("100%") 251 } 252} 253``` 254**参考链接** 255 256[设置窗口的显示方向属性](../reference/apis-arkui/arkts-apis-window-Window.md#setpreferredorientation9) 257[开启显示设备变化的监听](../reference/apis-arkui/js-apis-display.md#displayonaddremovechange) 258 259## 在display.on('change')监听回调中,无法使用Window实例获取更新后的窗口大小(API 10) 260 261**解决措施** 262 263旋转涉及[@ohos.window](../reference/apis-arkui/arkts-apis-window.md)和[@ohos.display](../reference/apis-arkui/js-apis-display.md)两个模块,处于不同进程。由于旋转完后display的更新时间早于window的更新时间(display旋转时直接宽高互换,提前可预知;window要等ArkUI布局完成才能确定窗口大小,耗时长),故在display触发变化时获取窗口信息会存在时序问题(窗口信息还未更新完成,此时使用Window实例获取到的还是原来的宽高)。应用可以通过display.on('change')接口监听显示设备变化,在callback中通过Display实例获取屏幕的width、height、orientation等信息。 264 265**错误示例** 266 267```ts 268// display先更新 269display.on('change', async (data) => { 270 let newDisplay: display.Display = display.getDefaultDisplaySync(); 271 console.info('Orientation: ' + newDisplay.orientation); 272 let windowClass: window.Window = await window.getLastWindow(this.context); 273 // window后更新,获取到的还是原来的宽高 274 let windowProperties = windowClass.getWindowProperties(); 275 console.info('Width: ' + windowProperties.windowRect.width + 276 ', height: ' + windowProperties.windowRect.height); 277 // 请确保已获取到相关Window实例,即windowClass 278 windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_CUTOUT); 279}); 280``` 281 282**正确示例** 283 284```ts 285display.on('change', (data) => { 286 console.info(`Succeeded in enabling the listener for display changes. Data: ${data}`); 287 let newDisplay: display.Display = display.getDefaultDisplaySync(); 288 console.info(`Orientation: ${newDisplay.orientation} , width: ${newDisplay.width} , height: ${newDisplay.height}`); 289}); 290``` 291 292**参考链接** 293 294[display.on('change')](../reference/apis-arkui/js-apis-display.md#displayonaddremovechange) 295 296## 如何同时获取屏幕方向orientation和系统规避区avoidAreaChange信息(API 10) 297 298可以通过[on('avoidAreaChange')](../reference/apis-arkui/arkts-apis-window-Window.md#onavoidareachange9)接口监听窗口系统规避区域的变化,在callback中获取avoidAreaChange信息,并通过Display实例获取屏幕方向orientation等信息。 299 300```ts 301// 请确保已获取到相关Window实例,即windowClass 302windowClass.on('avoidAreaChange', async (data) => { 303 console.info('Succeeded in enabling the listener for avoid area changes. Type: ' + 304 JSON.stringify(data.type) + ', area ' + JSON.stringify(data.area)); 305 let newDisplay: display.Display = display.getDefaultDisplaySync(); 306 console.info('Orientation: ' + newDisplay.orientation); 307 let windowClass: window.Window = await window.getLastWindow(this.context); 308 windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_CUTOUT); 309}); 310``` 311 312## 在display.on('foldDisplayModeChange')监听回调中,使用display.getDefaultDisplaySync获取当前屏幕宽高错误(API10) 313 314**解决措施** 315 316对于强制横屏应用,在设备发生折叠开合动作时,会触发foldDisplayModeChange,此时Display里对应旋转属性还未更新,导致通过getDefaultDisplaySync获取到的宽高为设备竖屏的宽高。应用可以通过display.on('change')接口监听显示设备变化,在回调函数中通过Display实例获取屏幕的宽高等信息。 317 318**代码示例** 319 320```ts 321display.on('change', (data) => { 322 console.info('Succeeded in enabling the listener for display changes. Data: ' + 323 JSON.stringify(data)); 324 let newDisplay: display.Display = display.getDefaultDisplaySync(); 325 console.info('width: ' + newDisplay.width + ', height: ' + newDisplay.height); 326}); 327``` 328 329**参考链接** 330 331[display.on('change')](../reference/apis-arkui/js-apis-display.md#displayonaddremovechange) 332 333## 窗口Orientation枚举值8\~10或12和枚举值13\~16的区别(API9) 334 3351. 窗口设置8\~10或12,会跟随传感器自动旋转,且受控制中心的旋转开关控制。 3362. 窗口设置13\~16,会临时旋转到指定方向(如:13会临时旋转到竖屏),之后跟随传感器自动旋转,受控制中心的旋转开关控制,且可旋转方向受系统判定。 337 338两者的区别是,调用13\~16时会临时旋转到指定方向,且前后台切换时窗口方向保持,而调用8\~10或12前后台切换时窗口方向不会保持。 339 340**场景举例:** 3411. 竖持手机,关闭旋转锁定开关 -> 应用设置方向为AUTO_ROTATION_RESTRICTED -> 将手机旋转为横屏(**应用方向为横屏**) -> 应用退出后台进入桌面,竖持手机(方向为竖屏) -> 应用切换至前台(**应用方向为竖屏**) 3422. 竖持手机,关闭旋转锁定开关 -> 应用设置方向为USER_ROTATION_PORTRAIT(**应用方向为竖屏**) -> 将手机旋转为横屏(**应用方向为横屏**) -> 应用退出后台进入桌面,竖持手机(方向为竖屏) -> 应用切换至前台(**应用方向为横屏**) 343 344| 名称 | 值 | 可旋转方向 | 是否跟随传感器自动旋转 | 是否受旋转开关控制 | 345|----------------------------------|----|-----------------|-------------|-----------| 346| AUTO_ROTATION_RESTRICTED | 8 | 横屏、竖屏、反向竖屏、反向横屏 | 是 | 是 | 347| AUTO_ROTATION_PORTRAIT_RESTRICTED | 9 | 竖屏、反向竖屏 | 是 | 是 | 348| AUTO_ROTATION_LANDSCAPE_RESTRICTED | 10 | 横屏、反向横屏 | 是 | 是 | 349| AUTO_ROTATION_UNSPECIFIED | 12 | 受系统判定 | 是 | 是 | 350| USER_ROTATION_PORTRAIT | 13 | 受系统判定 | 是 | 是 | 351| USER_ROTATION_LANDSCAPE | 14 | 受系统判定 | 是 | 是 | 352| USER_ROTATION_PORTRAIT_INVERTED | 15 | 受系统判定 | 是 | 是 | 353| USER_ROTATION_LANDSCAPE_INVERTED | 16 | 受系统判定 | 是 | 是 | 354 355<!--no_check-->