【HarmonyOS】应用设置全屏和安全区域详解

一、前言

IDE创建的鸿蒙应用,默认采取组件安全区布局方案。顶部会预留状态栏区域,底部会预留导航条区域。这就是所谓的安全区域。

如果不处理,界面效果很割裂。所以业内UI交互设计,都会设置应用为全屏布局。将页面绘制区域沾满整个界面。

或者将安全区域的颜色与应用UI设置为一致。

以上两种方式都是沉浸式布局的处理。所以全屏非沉浸式,概念不可混为一谈。
在移动应用开发中,"沉浸式效果"早已不是新鲜词,但要真正实现自然、和谐的沉浸式体验,却需要对系统布局、交互逻辑有深入理解。
在这里插入图片描述

二、什么是应用沉浸式效果?

简单来说,应用沉浸式效果是通过优化状态栏、应用界面与底部导航区域(导航条或三键导航)的视觉融合与交互适配,减少系统界面的突兀感,让用户注意力更聚焦于应用内容本身。

在这里插入图片描述

典型的界面元素包含三部分:
状态栏:显示时间、电量等系统信息的顶部区域
应用界面:承载应用核心内容的区域
底部导航区域:提供系统导航操作的底部区域

其中状态栏和底部导航区域被称为"避让区",其余区域为"安全区"。沉浸式开发的核心就是处理好这两个区域与应用内容的关系,主要涉及两类问题:
UI元素避让:避免可交互元素或关键信息被避让区遮挡
视觉融合:让避让区与应用界面的颜色、风格保持一致

三、如何设置沉浸式布局?

综上所述,我们可知,设置沉浸式布局有以下两种方式,如图所示:

在这里插入图片描述

1、方案一:窗口全屏布局方案

该方案通过将应用界面强制扩展到全屏(包括状态栏和导航区域),实现深度沉浸式体验。适合需要在避让区放置UI元素的场景,如视频播放器控制栏、游戏界面等。

场景1:保留避让区,需处理UI避让

当需要显示状态栏和导航区域,但希望应用内容延伸至这些区域时,需通过以下步骤实现:

(1)开启全屏布局
在应用启动时调用setWindowLayoutFullScreen接口,让界面突破安全区限制:

// EntryAbility.ets
let windowClass = windowStage.getMainWindowSync();
windowClass.setWindowLayoutFullScreen(true).then(() => {console.info("窗口已设置为全屏布局");
});

(2)获取并监听避让区尺寸
通过getWindowAvoidArea获取状态栏和导航区域高度,并注册avoidAreaChange监听动态变化(如屏幕旋转、折叠屏展开等场景):

// 获取状态栏高度
let systemArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
AppStorage.setOrCreate('statusBarHeight', systemArea.topRect.height);// 获取导航区域高度
let navArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
AppStorage.setOrCreate('navBarHeight', navArea.bottomRect.height);// 动态监听变化
windowClass.on('avoidAreaChange', (data) => {if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {AppStorage.setOrCreate('statusBarHeight', data.area.topRect.height);}
});

(3)布局中实现UI避让
在页面布局时,通过padding将内容区与避让区隔开,避免UI重叠:

// Index.ets
@Component
struct Index {@StorageProp('statusBarHeight') statusBarHeight: number = 0;@StorageProp('navBarHeight') navBarHeight: number = 0;build() {Column() {// 应用内容组件...}.padding({top: this.getUIContext().px2vp(this.statusBarHeight),bottom: this.getUIContext().px2vp(this.navBarHeight)})}
}
场景2:隐藏避让区,实现纯全屏

游戏、视频类应用常需要完全隐藏状态栏和导航区域,仅在用户操作时唤起:

(1)开启全屏布局(同场景1步骤1)
(2)隐藏系统栏
通过setSpecificSystemBarEnabled接口隐藏状态栏和导航区域:

// 隐藏状态栏
windowClass.setSpecificSystemBarEnabled('status', false);
// 隐藏导航区域
windowClass.setSpecificSystemBarEnabled('navigationIndicator', false);

(3)无需额外避让处理
此时界面已完全全屏,布局中无需设置避让padding,内容可直接铺满屏幕。

2、方案二:组件安全区方案

该方案为默认布局模式,UI元素自动限制在安全区内(无需手动处理避让),仅通过延伸背景绘制实现沉浸式效果。适合大多数普通应用,尤其是不需要在避让区布局UI的场景。

默认情况下,应用UI元素会自动避开避让区,但窗口背景可全屏绘制。通过以下方式优化视觉融合:

(1)状态栏与导航区域颜色相同时
直接设置窗口背景色与应用主背景一致,实现整体沉浸:

// EntryAbility.ets
windowStage.getMainWindowSync().setWindowBackgroundColor('#d5d5d5');

(2)颜色不同时:使用expandSafeArea扩展绘制
对顶部/底部组件单独设置expandSafeArea属性,使其背景延伸至避让区:

// Index.ets
@Component
struct Index {build() {Column() {// 顶部组件延伸至状态栏Row() {Text('顶部内容')}.backgroundColor('#2786d9').expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])// 中间内容区...// 底部组件延伸至导航区Row() {Text('底部内容')}.backgroundColor('#96dffa').expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])}}
}

(3)典型场景适配技巧

背景图/视频场景
让图片组件延伸至避让区

Image($r('app.media.bg')).width('100%').height('100%').expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

滚动容器场景
通过父容器扩展实现滚动背景沉浸

Scroll() {Column() {// 滚动内容...}.backgroundColor('rgb(213,213,213)')
}
.backgroundColor('rgb(213,213,213)')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

底部页签场景
Navigation/Tabs组件默认支持背景延伸,自定义页签可手动设置:

// 自定义底部页签
Row() {// 页签按钮...
}
.backgroundColor('#f5f5f5')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])

三、DEMO源码示例:

ImmersiveDemo/
├── src/main/ets/
│ ├── Ability/
│ │ └── EntryAbility.ets // 应用入口,处理窗口配置
│ ├── pages/
│ │ ├── FullScreenNormal.ets // 窗口全屏布局(不隐藏避让区)
│ │ ├── FullScreenHidden.ets // 窗口全屏布局(隐藏避让区)
│ │ └── SafeAreaMode.ets // 组件安全区方案
│ └── common/
│ └── Constants.ets // 常量定义

应用入口配置(EntryAbility.ets)


import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { pageMap } from '../common/Constants';export default class EntryAbility extends UIAbility {private mainWindow: window.Window | null = null;async onWindowStageCreate(windowStage: window.WindowStage) {// 获取主窗口实例this.mainWindow = windowStage.getMainWindowSync();if (!this.mainWindow) {console.error('获取主窗口失败');return;}// 加载首页windowStage.loadContent(pageMap.FULL_SCREEN_NORMAL, (err) => {if (err.code) {console.error(`加载页面失败: ${JSON.stringify(err)}`);return;}});// 初始化避让区数据监听this.initAvoidAreaListener();}// 初始化避让区监听private initAvoidAreaListener() {if (!this.mainWindow) return;// 初始获取避让区数据this.updateAvoidAreaData();// 监听避让区变化this.mainWindow.on('avoidAreaChange', (data) => {console.info(`避让区变化: ${JSON.stringify(data)}`);if (data.type === window.AvoidAreaType.TYPE_SYSTEM) {AppStorage.setOrCreate('statusBarHeight', data.area.topRect.height);} else if (data.type === window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) {AppStorage.setOrCreate('navBarHeight', data.area.bottomRect.height);}});}// 更新避让区数据到全局存储private updateAvoidAreaData() {if (!this.mainWindow) return;try {// 获取状态栏区域const systemArea = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);AppStorage.setOrCreate('statusBarHeight', systemArea.topRect.height);// 获取导航栏区域const navArea = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);AppStorage.setOrCreate('navBarHeight', navArea.bottomRect.height);} catch (err) {console.error(`获取避让区数据失败: ${JSON.stringify(err)}`);}}// 切换到窗口全屏布局(不隐藏避让区)模式public switchToFullScreenNormal() {if (!this.mainWindow) return;// 开启全屏布局this.mainWindow.setWindowLayoutFullScreen(true).then(() => {// 显示状态栏和导航栏this.mainWindow?.setSpecificSystemBarEnabled('status', true);this.mainWindow?.setSpecificSystemBarEnabled('navigationIndicator', true);// 加载对应页面this.context?.getWindowStage().then((stage) => {stage.loadContent(pageMap.FULL_SCREEN_NORMAL);});});}// 切换到窗口全屏布局(隐藏避让区)模式public switchToFullScreenHidden() {if (!this.mainWindow) return;// 开启全屏布局this.mainWindow.setWindowLayoutFullScreen(true).then(() => {// 隐藏状态栏和导航栏this.mainWindow?.setSpecificSystemBarEnabled('status', false);this.mainWindow?.setSpecificSystemBarEnabled('navigationIndicator', false);// 加载对应页面this.context?.getWindowStage().then((stage) => {stage.loadContent(pageMap.FULL_SCREEN_HIDDEN);});});}// 切换到组件安全区模式public switchToSafeAreaMode() {if (!this.mainWindow) return;// 关闭全屏布局(使用默认安全区布局)this.mainWindow.setWindowLayoutFullScreen(false).then(() => {// 显示状态栏和导航栏this.mainWindow?.setSpecificSystemBarEnabled('status', true);this.mainWindow?.setSpecificSystemBarEnabled('navigationIndicator', true);// 设置窗口背景色(用于安全区方案)this.mainWindow?.setWindowBackgroundColor('#d5d5d5');// 加载对应页面this.context?.getWindowStage().then((stage) => {stage.loadContent(pageMap.SAFE_AREA_MODE);});});}
}

2. 常量定义(Constants.ets)


export const pageMap = {FULL_SCREEN_NORMAL: 'pages/FullScreenNormal',FULL_SCREEN_HIDDEN: 'pages/FullScreenHidden',SAFE_AREA_MODE: 'pages/SafeAreaMode'
};

3. 窗口全屏布局(不隐藏避让区)页面


import { EntryAbility } from '../Ability/EntryAbility';
import { pageMap } from '../common/Constants';
import { UIContext } from '@kit.ArkUI';@Entry
@Component
struct FullScreenNormal {@StorageProp('statusBarHeight') statusBarHeight: number = 0;@StorageProp('navBarHeight') navBarHeight: number = 0;private uiContext: UIContext | null = null;build() {Column() {// 顶部导航栏Row() {Text('窗口全屏模式(不隐藏避让区)').fontSize(18).fontWeight(FontWeight.Bold).color(Color.White)}.backgroundColor('#2786d9').width('100%').height(50).justifyContent(FlexAlign.Center)// 内容区Scroll() {Column() {// 方案说明Text('此模式下界面延伸至状态栏和导航栏,但通过padding实现内容避让').fontSize(14).padding(15).backgroundColor('#e6f7ff').margin(10).borderRadius(8).width('90%')// 功能按钮区Column() {Button('切换到全屏隐藏模式').width('80%').margin(5).onClick(() => {(getContext(this) as any).ability.switchToFullScreenHidden();})Button('切换到组件安全区模式').width('80%').margin(5).onClick(() => {(getContext(this) as any).ability.switchToSafeAreaMode();})}.margin(20)// 示例内容卡片ForEach([1, 2, 3, 4], (item) => {Row() {Text(`内容卡片 ${item}`).fontSize(16).color('#333')}.backgroundColor(Color.White).width('90%').height(100).borderRadius(10).margin(10).justifyContent(FlexAlign.Center)})}.width('100%')}// 底部信息栏Row() {Text('底部操作区').fontSize(16).color(Color.White)}.backgroundColor('#96dffa').width('100%').height(60).justifyContent(FlexAlign.Center)}.width('100%').height('100%').backgroundColor('#d5d5d5').padding({top: this.uiContext ? this.uiContext.px2vp(this.statusBarHeight) : 0,bottom: this.uiContext ? this.uiContext.px2vp(this.navBarHeight) : 0}).onAppear(() => {this.uiContext = this.getUIContext();})}
}

4. 窗口全屏布局(隐藏避让区)页面


import { pageMap } from '../common/Constants';@Entry
@Component
struct FullScreenHidden {build() {Column() {// 顶部区域Row() {Text('全屏隐藏模式').fontSize(18).fontWeight(FontWeight.Bold).color(Color.White)}.backgroundColor('#2786d9').width('100%').height(50).justifyContent(FlexAlign.Center)// 内容区Scroll() {Column() {// 提示信息Text('状态栏和导航栏已隐藏,上滑底部可唤起导航栏').fontSize(14).padding(15).backgroundColor('#fff3cd').margin(10).borderRadius(8).width('90%')// 功能按钮区Column() {Button('切换到全屏普通模式').width('80%').margin(5).onClick(() => {(getContext(this) as any).ability.switchToFullScreenNormal();})Button('切换到组件安全区模式').width('80%').margin(5).onClick(() => {(getContext(this) as any).ability.switchToSafeAreaMode();})}.margin(20)// 模拟视频播放区域Row() {Text('视频播放区域').fontSize(20).color(Color.White)}.backgroundColor('#333').width('90%').height(200).borderRadius(10).margin(10).justifyContent(FlexAlign.Center)// 示例内容卡片ForEach([1, 2, 3], (item) => {Row() {Text(`内容卡片 ${item}`).fontSize(16).color('#333')}.backgroundColor(Color.White).width('90%').height(100).borderRadius(10).margin(10).justifyContent(FlexAlign.Center)})}.width('100%')}// 底部操作区Row() {Text('播放控制区').fontSize(16).color(Color.White)}.backgroundColor('#96dffa').width('100%').height(60).justifyContent(FlexAlign.Center)}.width('100%').height('100%').backgroundColor('#d5d5d5')}
}

5. 组件安全区方案页面


import { SafeAreaEdge, SafeAreaType } from '@kit.ArkUI';
import { pageMap } from '../common/Constants';@Entry
@Component
struct SafeAreaMode {build() {Column() {// 顶部导航栏(延伸至状态栏)Row() {Text('组件安全区模式').fontSize(18).fontWeight(FontWeight.Bold).color(Color.White)}.backgroundColor('#2786d9').width('100%').height(50).justifyContent(FlexAlign.Center).expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP]) // 延伸至状态栏// 内容区Scroll() {Column() {// 方案说明Text('此模式下UI元素自动限制在安全区,通过expandSafeArea延伸背景至避让区').fontSize(14).padding(15).backgroundColor('#e6f7ff').margin(10).borderRadius(8).width('90%')// 功能按钮区Column() {Button('切换到全屏普通模式').width('80%').margin(5).onClick(() => {(getContext(this) as any).ability.switchToFullScreenNormal();})Button('切换到全屏隐藏模式').width('80%').margin(5).onClick(() => {(getContext(this) as any).ability.switchToFullScreenHidden();})}.margin(20)// 示例内容卡片ForEach([1, 2, 3, 4], (item) => {Row() {Text(`内容卡片 ${item}`).fontSize(16).color('#333')}.backgroundColor(Color.White).width('90%').height(100).borderRadius(10).margin(10).justifyContent(FlexAlign.Center)})}.width('100%')}// 底部信息栏(延伸至导航区)Row() {Text('底部导航区').fontSize(16).color(Color.White)}.backgroundColor('#96dffa').width('100%').height(60).justifyContent(FlexAlign.Center).expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) // 延伸至导航区}.width('100%').height('100%').backgroundColor('#d5d5d5')}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95996.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95996.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95996.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openfeign 只有接口如何创建bean的

OpenFeign 能够为纯接口创建 Spring Bean,其核心机制是通过动态代理和 Spring 的 FactoryBean 机制实现的。以下是详细的工作原理:1. EnableFeignClients 注解的启动在 Spring Boot 主类上添加 EnableFeignClients 注解:SpringBootApplicatio…

【展厅多媒体】互动地砖屏怎么提升展厅互动感的?

在数字化展厅设计中,互动地砖屏 正成为提升观众参与度的重要工具。这种融合视觉科技与交互体验的装置,通过动态影像与即时反馈,让参观者从被动观看转变为主动探索,从而大幅增强展厅的互动感。 Led地面互动屏的优势在于其强大的视…

AI赋能电力巡检:变压器漏油智能检测系统全解析

🔥 AI赋能电力巡检:变压器漏油智能检测系统全解析 📖 前言 在电力系统的日常运维中,变压器作为核心设备,其安全运行直接关系到整个电网的稳定性。传统的人工巡检方式不仅效率低下,还存在安全隐患和漏检风险…

GitHub上值得Star的计算机视觉项目

GitHub上值得Star的计算机视觉项目 前言 一、OpenCV:计算机视觉领域的瑞士军刀 1.1 项目简介 1.2 核心功能与技术特点 1.3 代码示例 二、YOLO 系列:实时目标检测的领导者 2.1 项目简介 2.2 核心功能与技术特点 2.3 代码示例 三、Detectron2:Facebook AI Research 的目标检测…

【深度学习】pytorch深度学习框架的环境配置

文章目录1. 配置cuda环境2. 配置conda环境3. 配置pytorch gpu环境1. 配置cuda环境 在命令行输入以下命令可以查看当前显卡驱动版本和最高支持的cuda版本 nvidia-smi根据cuda版本去官网下载并安装cuda 下载链接:https://developer.nvidia.com/cuda-toolkit-archive…

数据处理与统计分析 —— 房源数据集分析案例

数据集网盘下载: 链接:https://pan.quark.cn/s/0e577858dba3?pwdFJnb 提取码:FJnb代码仅供参考具体可打开ipynb文件进行学习和练习:链接:https://pan.quark.cn/s/8efbe3061fad?pwdT47B 提取码:T47Bimport…

蓝牙如何测试?

车载蓝牙测试需覆盖 连接稳定性、功能完整性、兼容性、交互体验等核心维度,结合车载场景的特殊性(如行驶中信号干扰、多设备交互、安全需求),具体测试点如下: 一、基础配对与连接测试 1. 首次配对 触发配对:车机端 “蓝牙设置” 中搜索设备、手机端搜索车机(车机名称是…

算法02 二进制与位运算

二进制作为计算机底层数据的核心表示方式,其独特的位结构和运算规则在算法设计中有着广泛且关键的应用。以下从基础操作、算法技巧、数据结构、经典问题等多个维度,全面梳理二进制在算法中的应用: 一、基础位运算:算法的“原子操作…

PAT 1071 Speech Patterns

题目大意是说给出一个文本,找出里面出现最多的单词,如果有多个单词出现次数一样多,则输出字典序最小的。 需要注意的是: 给出的文本字符串不仅有数字还有字母,还有一些特殊的字符,还有空格。 而单词是只包含…

CSS中的 :root 伪类

在CSS中&#xff0c;伪类是一种用于选择元素特定状态的选择器。:root 伪类专门用于选择文档的根元素&#xff08;在HTML中通常是<html>元素&#xff09;&#xff0c;它是CSS变量&#xff08;Custom Properties&#xff09;的理想载体&#xff0c;常用于定义全局样式变量&…

能源行业数字化转型:边缘计算网关在油田场景的深度应用

能源行业数字化转型&#xff1a;边缘计算网关在油田场景的深度应用能源行业是国民经济的支柱产业&#xff0c;而油田作为能源生产的重要基地&#xff0c;其数字化转型对于提高生产效率、降低能耗、减少碳排放具有重要意义。然而&#xff0c;油田往往地处偏远&#xff0c;油井分…

CAG缓存增强生成与RAG检索增强生成对比

深度定制 LLM 知识,除了 RAC &#xff0c;现在又有新技术假设有一份200页的产品手册,你想让 LLM 准确回答里面的相关问题,要实现这个目标,除了常用的检索增强生成技术 rep ,现在有了新思路,缓存增强生成 CAG &#xff0c;它是什么,何时使用.RAG检索增强是常规套路,CAG缓存增强是…

基于vue、node.js、express的网络教学系统设计与实现/基于vue、node.js、express的在线学习系统设计与实现

基于vue、node.js、express的网络教学系统设计与实现/基于vue、node.js、express的在线学习系统设计与实现

享元模式引发的关于ECS和对象池的思考记录

文章目录概念概述解决了什么区别与联系享元模式的某个例子的细节分析概念概述 ECS&#xff08;Entity-Component-System&#xff09; 1、Entity&#xff08;实体&#xff09;&#xff1a;唯一标识符。 2、Component&#xff08;组件&#xff09;&#xff1a;纯数据容器&#x…

STM32驱动SG90舵机全解析:从PWM原理到多舵机协同控制

一、SG90舵机核心特性 1.1 基本参数与选型 SG90作为​​微型舵机的代表​​,凭借其​​轻量化设计​​(仅9g)和​​高性价比​​,在机器人、智能小车和云台系统中广泛应用: ​​关键参数对比​​: ​​参数​​ 180定位舵机 360连续旋转舵机 ​​控制目标​​ 精确…

goland怎么取消自动删除未使用的包

1.settings-Go-Imports-取消勾选Optimize imports on the fly2.settings-Tools-取消勾选Optimize imports

halcon基于透视的可变形模型匹配

算子1&#xff0c;create_planar_uncalib_deformable_model_xld***基于平面未校准的轮廓模型算子2&#xff0c;find_planar_uncalib_deformable_model***查找平面未校准可变形模型算子3&#xff0c;projective_trans_contour_xld***将轮廓进行透视变换附加算子 算子4read_conto…

Flink Stream API - 源码开发需求描述

概述 本文介绍如何基于Flink源码进行二次开发&#xff0c;实现一个动态规则引擎系统。通过自定义算子和算子协调器&#xff0c;实现数据流的动态规则计算和协调管理。以此更好理解前面介绍的源码相关文章 项目需求 核心功能 实现一个动态规则引擎&#xff0c;具备以下特性&…

「 CentOS7 安装部署k8s」

一、Linux系统部署K8s还是非常便利的&#xff0c;只需要掌握Linux常用命令&#xff0c;便可以迅速部署&#xff0c;一起来学习一下吧1、运行以下命令更新系统并安装必要工具&#xff1a;yum update -y yum install -y yum-utils device-mapper-persistent-data lvm22、安装Dock…

Disbursement on Quarantine Policy(概率、逆元计算期望)

题目描述There is a train with n rows, and there are m seats per row. All seats are occupied. For some passengers, we know they are being infected with COVID-19 or not. However, for other passengers, we are not sure about their status, and we assume each of…