1# 沉浸式界面开发 2 3## 场景说明 4沉浸式界面通常是指全屏显示,即当前画面占据整个屏幕。画面放大的同时,让用户摆脱无关信息的干扰,带给用户沉浸式的体验。常见的场景有:视频播放、游戏等。本例即为大家介绍如何开发沉浸式界面。 5 6> **说明:** 7> 8> 当前沉浸式界面开发仅支持window级别的配置,暂不支持Page级别的配置。开发者若有Page级别切换的需要,可以在页面的生命周期开始,例如onPageShow中设置沉浸模式,然后在页面退出,例如onPageHide中恢复默认设置来实现。 9 10## 效果呈现 11本例中的沉浸式界面有三种实现方式,对应效果如下: 12 13| 方案一:颜色背景铺满 | 方案二:图片背景铺满 | 方案三:背景铺满的同时、状态栏不可见 | 14| ----------------------------------- | --------------------------------------------- | ------------------------------------- | 15|  |  |  | 16 17 18## 运行环境 19本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发: 20 21- IDE: DevEco Studio 3.1 Beta2 22- SDK: Ohos_sdk_public 3.2.11.9(API Version 9 Release) 23 24## 实现思路 25如果一个应用想要获得沉浸式的体验,开发者可以通过以下三种方式进行实现: 26 27- 颜色背景通铺:使应用页面的背景色和状态栏、导航栏的背景色一致。可通过setWindowSystemBarProperties进行设置。 28- 图片背景通铺:将状态栏、导航栏的背景色设置为透明以便呈现应用界面的背景,同时通过 windowClass.on接口获取到状态栏、导航栏的区域信息,进行规避处理,以免状态栏、导航栏的内容遮挡住应用内容。 29- 隐藏导航栏和状态栏:使用setWindowSystemBarEnable设置导航栏和状态栏为隐藏状态。 30 31> **说明:** 32> 33> 沉浸式的设置最好放在ability的onWindowStageCreate的生命周期里,此时刚好可以获取窗口的信息,放在页面生命周期里会出现窗口大小不一致,影响体验。 34 35下文将分别介绍这三种方案的具体开发步骤。 36 37 38## 开发步骤 39### 颜色背景通铺 40此方案通过调用setWindowSystemBarProperties接口将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色,以达到界面全屏的效果。 41 42具体代码如下: 43 44```ts 45import window from '@ohos.window'; 46import common from '@ohos.app.ability.common'; 47 48@Entry 49@Component 50struct Type2 { 51 @State message: string = 'Hello World' 52 // 获取UIAbility上下文 53 context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext 54 async setSystemBar() { 55 // 获取当前应用窗口 56 let windowClass:window.Window = await window.getLastWindow(context) 57 // 将状态栏和导航栏的背景色设置为跟应用窗口相同的颜色 58 await windowClass.setWindowSystemBarProperties({ 59 navigationBarColor: "#00FF00", 60 statusBarColor: "#00FF00", 61 navigationBarContentColor: "#00FF00", 62 statusBarContentColor: "#00FF00" 63 }) 64 } 65 66 aboutToAppear() { 67 this.setSystemBar() 68 } 69 70 build() { 71 Row() { 72 Column() { 73 Text(this.message) 74 .fontSize(50) 75 .fontWeight(FontWeight.Bold) 76 } 77 .width('100%') 78 } 79 .height('100%') 80 } 81} 82``` 83此方案的优势在于不需要处理应用窗口和状态栏、导航栏窗口的遮挡关系,因为此方案没有使用setWindowLayoutFullScreen 接口设置沉浸式布局,所以三个窗口是平铺的,不会重叠。劣势在于无法将应用的背景图等信息延伸到状态栏、导航栏窗口中。适用于扁平化设计风格的应用。 84 85### 图片背景通铺 86这种方案可以实现图片背景的通铺,同时又能避免状态栏和导航栏的内容跟应用内容相互遮挡,导致显示效果异常。 87为了能让应用的有效显示范围避开系统的状态栏和导航栏,以免内容重叠,我们可以通过windowClass.on(type: ‘avoidAreaChange’, callback: Callback<{AvoidAreaType, AvoidArea}>) 获取系统规避区域的大小,并对这一块区域做出相应的规避。 88其中回调参数AvoidArea是规避区域,可以通过其获取规避区域的具体范围;AvoidAreaType是规避区域的类型其取值如下,示例中需要规避的状态栏和导航栏属于TYPE_SYSTEM类型。 89 90| 名称 | 值 | 说明 | 91| -------------------- | ---- | ----------------- | 92| TYPE_SYSTEM | 0 | 表示系统默认区域。 | 93| TYPE_CUTOUT | 1 | 表示刘海屏区域。 | 94| TYPE_SYSTEM_GESTURE9+ | 2 | 表示手势区域。 | 95| TYPE_KEYBOARD9+ | 3 | 表示软键盘区域 | 96具体代码如下: 97 98**page代码** 99 100```ts 101// index.ets 102@Entry 103@Component 104struct Type3 { 105 @State message: string = 'Hello World' 106 @StorageLink("topHeight") topHeight: number = 0 107 @StorageLink("bottomHeight") bottomHeight: number = 0 108 109 build() { 110 Column() { 111 // 在界面顶部放置一个Row组件,用于占位 112 Row() { 113 114 } 115 .width("100%") 116 // 设置Row组件的高度为状态栏的高度,可避免界面内容与状态栏内容重叠 117 .height(px2vp(this.topHeight)) 118 Row() { 119 Text(this.message) 120 .fontSize(50) 121 .fontWeight(FontWeight.Bold) 122 .position({ x: 0, y: 0 }) 123 } 124 .width("100%") 125 .flexGrow(1) 126 // 在界面底部放置一个Row组件,用于占位 127 Row() { 128 129 } 130 .width("100%") 131 // 设置Row组件的高度为导航栏的高度,可避免界面内容与导航栏内容重叠 132 .height(px2vp(this.bottomHeight)) 133 } 134 .backgroundImage($r("app.media.icon")) 135 .backgroundImageSize(ImageSize.Cover) 136 .width("100%") 137 .height("100%") 138 } 139} 140``` 141**ability代码** 142```ts 143// MainAbility.ts 144import window from '@ohos.window'; 145 146async function enterImmersion(windowClass: window.Window) { 147 // 获取状态栏和导航栏的高度 148 windowClass.on("avoidAreaChange", ({ type, area }) => { 149 if (type == window.AvoidAreaType.TYPE_SYSTEM) { 150 // 将状态栏和导航栏的高度保存在AppStorage中 151 AppStorage.SetOrCreate<number>("topHeight", area.topRect.height); 152 AppStorage.SetOrCreate<number>("bottomHeight", area.bottomRect.height); 153 } 154 }) 155 // 设置窗口布局为沉浸式布局 156 await windowClass.setWindowLayoutFullScreen(true) 157 await windowClass.setWindowSystemBarEnable(["status", "navigation"]) 158 // 设置状态栏和导航栏的背景为透明 159 await windowClass.setWindowSystemBarProperties({ 160 navigationBarColor: "#00000000", 161 statusBarColor: "#00000000", 162 navigationBarContentColor: "#FF0000", 163 statusBarContentColor: "#FF0000" 164 }) 165} 166 167export default class MainAbility extends Ability { 168 ... 169 async onWindowStageCreate(windowStage: window.WindowStage) { 170 let windowClass:window.Window = await windowStage.getMainWindow() 171 await enterImmersion(windowClass) 172 windowStage.loadContent('pages/page5') 173 } 174 ... 175} 176``` 177类似的,若需获取刘海屏遮挡区域,需要将上述代码中注册的监听类型从TYPE_SYSTEM替换为TYPE_CUTOUT,并解析返回的[AvoidArea](../application-dev/reference/apis-arkui/js-apis-window.md#avoidarea7)来进行窗口内容避让。 178 179### 隐藏状态栏、导航栏 180隐藏状态栏、导航栏可以达到完全沉浸的效果,使用setWindowSystemBarEnable接口即可实现。 181 182具体代码如下: 183 184```ts 185import window from '@ohos.window'; 186import common from '@ohos.app.ability.common'; 187 188@Entry 189@Component 190struct Type3 { 191 @State message: string = 'Hello World' 192 context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext 193 async setSystemBar() { 194 let windowClass = await window.getLastWindow(context) 195 //设置导航栏,状态栏不可见 196 await windowClass.setWindowSystemBarEnable([]) 197 } 198 199 aboutToAppear() { 200 this.setSystemBar() 201 } 202 203 build() { 204 Row() { 205 Column() { 206 Text(this.message) 207 .fontSize(50) 208 .fontWeight(FontWeight.Bold) 209 } 210 .width('100%') 211 } 212 .backgroundColor("#ffee33") 213 .height('100%') 214 } 215} 216``` 217 218## 参考 219[窗口](../application-dev/reference/apis-arkui/js-apis-window.md)