1# 沉浸式界面开发
2
3## 场景说明
4沉浸式界面通常是指全屏显示,即当前画面占据整个屏幕。画面放大的同时,让用户摆脱无关信息的干扰,带给用户沉浸式的体验。常见的场景有:视频播放、游戏等。本例即为大家介绍如何开发沉浸式界面。
5
6> **说明:**
7>
8> 当前沉浸式界面开发仅支持window级别的配置,暂不支持Page级别的配置。开发者若有Page级别切换的需要,可以在页面的生命周期开始,例如onPageShow中设置沉浸模式,然后在页面退出,例如onPageHide中恢复默认设置来实现。
9
10## 效果呈现
11本例中的沉浸式界面有三种实现方式,对应效果如下:
12
13| 方案一:颜色背景铺满                | 方案二:图片背景铺满                          | 方案三:背景铺满的同时、状态栏不可见  |
14| ----------------------------------- | --------------------------------------------- | ------------------------------------- |
15| ![fullcolor](figures/fullcolor.PNG) | ![fullbackground](figures/fullbackground.PNG) | ![fullscreen](figures/fullscreen.PNG) |
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)