【HarmonyOS】應用設置全屏和安全區域詳解

【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…