沉浸式模式是一种旨在减少无关元素干扰,使应用界面更专注于内容呈现的设计模式。在典型全屏窗口中,状态栏和底部导航条被称为"避让区",其余区域为"安全区"。该模式的核心在于将应用页面延伸至避让区,以实现三大目的:首先是视觉统一,通过使页面与避让区色调一致,避免界面割裂;其次是布局扩展,充分利用屏幕可视区域,获得更大的布局空间;最后是沉浸体验,在游戏或视频等特定场景下隐藏系统元素,提供无干扰的全屏享受。
实现方案
(一)设置窗口的全屏模式
调用窗口强制全屏布局方法setWindowLayoutFullScreen()设置窗口为沉浸式布局。页面布局范围从安全区域扩展为整个窗口(包括状态栏和导航条),但该方案为窗口级沉浸方案,当沉浸式布局生效时,应用中所有页面均开启全屏模式,需要开发者在各页面进行避让处理,不然可能出现元素遮挡影响用户体验。
方法参数:
setWindowLayoutFullScreen(isLayoutFullScreen: boolean): Promise< void >
该方法使用Promise异步回调,当沉浸式布局生效时,布局不避让状态栏与底部导航区域,组件可能产生与其重叠的情况,而非沉浸式布局生效时,布局避让状态栏与底部导航区域,组件不会与其重叠。在HarmonyOS 5.0.2之前,该接口在所有设备中可正常调用。
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| isLayoutFullScreen | boolean | 是 | 窗口的布局是否为沉浸式布局(该沉浸式布局状态栏、底部导航区域仍然显示)。true表示沉浸式布局;false表示非沉浸式布局。 |
返回值:
| 类型 | 说明 |
|---|---|
| Promise< void > | 无返回结果的Promise对象。 |
官方示例:
ts
// EntryAbility.ets
import { UIAbility } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
// ...
onWindowStageCreate(windowStage: window.WindowStage): void {
console.info('onWindowStageCreate');
let windowClass: window.Window | undefined = undefined;
windowStage.getMainWindow((err: BusinessError, data) => {
const errCode: number = err.code;
if (errCode) {
console.error(`Failed to obtain the main window. Cause code: ${err.code}, message: ${err.message}`);
return;
}
windowClass = data;
let isLayoutFullScreen = true;
try {
let promise = windowClass.setWindowLayoutFullScreen(isLayoutFullScreen);
promise.then(() => {
console.info('Succeeded in setting the window layout to full-screen mode.');
}).catch((err: BusinessError) => {
console.error(`Failed to set the window layout to full-screen mode. Cause code: ${err.code}, message: ${err.message}`);
});
} catch (exception) {
console.error(`Failed to set the window layout to full-screen mode. Cause code: ${exception.code}, message: ${exception.message}`);
}
});
}
}
(二)扩展组件到安全区域外
设置组件的expandSafeArea属性,将组件的安全区域延伸至状态栏或导航条区域,同时保持子组件在安全区内布局,无需额外避让处理。支持指定系统避让区域类型和延伸方向,实现沉浸式的方案可参考组件安全区方案。
在使用
expandSafeArea属性时,建议组件尺寸不要设置固定宽高(百分比除外)。若设置固定宽高,扩展方向仅支持顶部和起始侧,且尺寸保持不变。当父容器为滚动容器时,该属性本身不直接生效,需配合子节点设置。组件必须与安全区域边界重合才能生效,且仅作用于当前组件,不会传递给父子组件。同时,若存在position等属性,会优先生效,可能影响扩展效果。
属性参数:
expandSafeArea(types?: Array, edges?: Array): T
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| types | Array | 否 | 配置扩展安全区域的类型。未添加Metadata配置项时,页面不避让挖孔,CUTOUT类型不生效。 默认值:[SafeAreaType.SYSTEM, SafeAreaType.CUTOUT, SafeAreaType.KEYBOARD] 非法值:按默认值处理。 |
| edges | Array | 否 | 配置扩展安全区域的边缘。 默认值:[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM, SafeAreaEdge.START, SafeAreaEdge.END] 非法值:按默认值处理。扩展至所有避让区域。 |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
SafeAreaType
扩展安全区域的枚举类型。
名称 值 说明 SYSTEM 0 系统默认非安全区域,包括状态栏、导航栏。 CUTOUT 1 设备的非安全区域,例如刘海屏或挖孔屏区域。 KEYBOARD 2 软键盘区域。 SafeAreaEdge
扩展安全区域的边缘。
名称 值 说明 TOP 0 上方区域。 BOTTOM 1 下方区域。 START 2 前部区域。 END 3 尾部区域。
官方示例:
ts
// xxx.ets
@Entry
@Component
struct SafeAreaExample1 {
@State text: string = ''
controller: TextInputController = new TextInputController()
build() {
Row() {
Column()
.width('100%')
.height('100%')
// $r('app.media.bg')需要替换为开发者所需的图像资源文件
.backgroundImage($r('app.media.bg'))
.backgroundImageSize(ImageSize.Cover)
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}.height('100%')
}
}
(三)组件背景沉浸
当组件与避让区边界重合时,利用 background() 属性可将组件背景延伸至状态栏或导航条等避让区域,从而实现视觉上的沉浸感,而页面的实际布局仍保留在安全区内。这一方法属于组件级设置,允许不同组件根据需求定义各异的背景色并分别扩展至对应的避让区;若追求整体统一,亦可在最外层父组件设置该属性,以适配多样的窗口模式与方向,达成全屏沉浸效果。需要注意的是,这种扩展仅限于视觉绘制层面,界面元素无法真正布局到系统UI区域,且在页面涉及滚动时,滚动内容无法延伸至系统的避让区。
属性参数:
background(content: CustomBuilder | ResourceColor, options?: BackgroundOptions): T
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| content | CustomBuilder |ResourceColor | 是 | 自定义背景。 |
| options | BackgroundOptions | 否 | 设置自定义背景选项。**说明:**API version 20之前,options:{align?: Alignment} |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
在使用自定义背景渲染时,需注意其存在一定的延迟且无法响应事件,同时不支持嵌套使用,且CustomBuilder类型的背景在预览器中无法预览。从API version 20开始,背景支持动态更新。当同时设置background、backgroundColor和backgroundImage时,若background为ResourceColor类型或设置了ignoresLayoutSafeAreaEdges属性,则background位于最底层,否则位于最上层。此外,若background的content参数为CustomBuilder类型,background不会随CustomBuilder内容的更新而变化。
ResourceColor
type ResourceColor = Color | number | string | Resource
颜色类型,用于描述资源颜色类型。
类型 说明 Color 颜色枚举值。 number HEX格式颜色,支持rgb或者argb。 string 支持rgb、rgba或者argb的格式颜色。 Resource 使用引入资源的方式,引入系统资源或者应用资源中的颜色。 BackgroundOptions对象说明
background配置选项。
名称 类型 说明 align Alignment 自定义背景与组件的对齐方式。 ignoresLayoutSafeAreaEdges Array<LayoutSafeAreaEdge> 配置背景要扩展到的安全区,包括:状态栏,导航栏和safeAreaPadding。
(四)组件设置页面沉浸
通过设置ignoreLayoutSafeArea()并设置高度为LayoutPolicy.matchParent来适应父组件,页面背景与布局均扩展至顶部状态栏和底部导航条。但每个页面均需单独配置,并且当页面内容与避让区发生冲突时,需由开发者手动进行避让处理。
方法参数:
ignoreLayoutSafeArea(types?: Array, edges?: Array): T
参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| types | Array | 否 | 扩展布局安全区域的类型。默认值:[LayoutSafeAreaType.SYSTEM],扩展至所有安全区域,比如:状态栏,导航栏和组件级安全区(safeAreaPadding)。非法值:按默认值处理。 |
| edges | Array | 否 | 扩展布局安全区的边缘,并且支持镜像能力。默认值:[LayoutSafeAreaEdge.ALL],扩展组件所有边缘。非法值:按默认值处理。 |
返回值:
| 类型 | 说明 |
|---|---|
| T | 返回当前组件。 |
当组件设置
ignoreLayoutSafeArea属性时,若其宽高采用了LayoutPolicy.matchParent,其尺寸与位置均会发生改变,否则仅位置改变。结合safeAreaPadding的累积特性,组件可将安全区边缘扩展至所有连续感知的安全区域。对于 List、Grid 等滚动类组件的子元素,若忽略布局安全区,则在滚动方向上不考虑滚动组件及其父组件的安全区域。若同时设置.ignoreLayoutSafeArea和.expandSafeArea,前者优先生效,后者在其基础上进行绘制扩展。LayoutSafeAreaType
扩展布局安全区域的枚举类型。
名称 值 说明 SYSTEM 0 设置后,组件的布局范围可扩展至组件级安全区(safeAreaPadding)和页面级安全区(状态栏、导航栏、挖孔区)。 LayoutSafeAreaEdge
扩展安全区域的边缘。
名称 值 说明 TOP 0 上方区域。 BOTTOM 1 下方区域。 START 2 前部区域。LTR模式时表示左侧区域,RTL模式表示右侧区域。 END 3 尾部区域。LTR模式时表示右侧区域,RTL模式表示左侧区域。 VERTICAL 4 垂直区域。 HORIZONTAL 5 水平区域。 ALL 6 全部区域。
官方示例代码:
ts
import { LengthMetrics } from '@kit.ArkUI'
@Entry
@Component
struct IgnoreLayoutSafeAreaTest1 {
build() {
Column() {
Stack() {
Row()
.backgroundColor('rgb(39, 135, 217)')
.width(75) // 固定宽度
.height(75) // 固定高度
.ignoreLayoutSafeArea([LayoutSafeAreaType.SYSTEM], [LayoutSafeAreaEdge.START, LayoutSafeAreaEdge.TOP]) // 设置布局区域延伸取左和上方向,至系统避让区SYSTEM
Row()
.backgroundColor('rgb(0, 74, 175)')
.width(75)
.height(75)
}
.width(200)
.height(200)
.backgroundColor(Color.Gray)
.align(Alignment.TopStart) // 子组件相对于Stack容器左上对齐
.padding({
left: 10 // 设置左侧10vp普通内边距
})
.safeAreaPadding(LengthMetrics.vp(10)) // 设置10vp安全区内边距(即组件级安全区)
}
.width('100%')
}
}