:Android、iOS 與鴻蒙安全區(qū)域適配實戰(zhàn))
1. 引言在移動端 H5 應用開發(fā)中安全區(qū)域Safe Area適配是繞不開的難題。隨著全面屏、劉海屏、挖孔屏以及靈動島的普及頁面底部和頂部常常被系統(tǒng)手勢條、狀態(tài)欄或攝像頭區(qū)域遮擋導致按鈕不可點、文字被截斷。本文圍繞 Android、iOS 和鴻蒙HarmonyOS三大平臺系統(tǒng)講解安全區(qū)域適配的原理與最佳實踐。2. 什么是安全區(qū)域安全區(qū)域是指系統(tǒng)為應用內(nèi)容預留的、不會被狀態(tài)欄、導航欄、手勢條、圓角或劉海遮擋的可視區(qū)域。不同平臺對安全區(qū)域的定義和獲取方式各不相同但目標一致讓關鍵內(nèi)容始終完整可見、可交互。常見需要適配的場景包括頂部狀態(tài)欄與劉海區(qū)域遮擋標題欄。底部手勢條Home Indicator遮擋操作按鈕。橫屏時左右兩側(cè)的圓角與劉海區(qū)域。鍵盤彈出時底部輸入框被遮擋。3. iOS 安全區(qū)域適配iOS 從 iPhone X 開始引入劉海屏系統(tǒng)通過safe-area-inset-*環(huán)境變量向 WebView 暴露安全區(qū)域信息適配最為規(guī)范。3.1 viewport 設置在 HTML 的 viewport meta 標簽中必須設置viewport-fitcover否則頁面默認不會延伸到安全區(qū)域之外也就無法讀取安全區(qū)數(shù)值。meta nameviewport contentwidthdevice-width, initial-scale1.0, viewport-fitcover /3.2 CSS env() 函數(shù)設置 viewport-fitcover 后即可通過 CSS 的env()函數(shù)讀取四個方向的安全區(qū) inset 值。.safe-top { padding-top: env(safe-area-inset-top); } .safe-bottom { padding-bottom: env(safe-area-inset-bottom); } .safe-left { padding-left: env(safe-area-inset-left); } .safe-right { padding-right: env(safe-area-inset-right); }對于不支持 env() 的舊版本瀏覽器建議同時提供constant()作為降級方案。.safe-bottom { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); }3.3 底部固定按鈕適配底部固定操作欄是最常見的適配場景需要同時考慮手勢條高度和底部安全區(qū)。.bottom-bar { position: fixed; left: 0; right: 0; bottom: 0; padding-bottom: env(safe-area-inset-bottom); background-color: #ffffff; }4. Android 安全區(qū)域適配Android 生態(tài)碎片化嚴重不同廠商對劉海屏、挖孔屏的處理方式不同WebView 對安全區(qū)域的暴露也不如 iOS 統(tǒng)一。Android 端通常需要結(jié)合原生代碼與 CSS 變量共同處理。4.1 原生側(cè)獲取安全區(qū)在 Android 原生層可以通過WindowInsets獲取系統(tǒng)欄和安全區(qū)域信息再通過 JavaScript 橋接注入到 H5 頁面。ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets - val systemBars insets.getInsets(WindowInsetsCompat.Type.systemBars()) val displayCutout insets.getInsets(WindowInsetsCompat.Type.displayCutout()) val top maxOf(systemBars.top, displayCutout.top) val bottom maxOf(systemBars.bottom, displayCutout.bottom) // 通過 WebView 注入 JS webView.evaluateJavascript( window.safeArea { top: $top, bottom: $bottom, left: 0, right: 0 };, null ) insets }4.2 前端讀取注入值原生注入后前端通過讀取window.safeArea動態(tài)設置 CSS 變量實現(xiàn)安全區(qū)適配。function applySafeArea() { const safe window.safeArea || { top: 0, bottom: 0, left: 0, right: 0 }; const root document.documentElement; root.style.setProperty(--safe-top, safe.top px); root.style.setProperty(--safe-bottom, safe.bottom px); root.style.setProperty(--safe-left, safe.left px); root.style.setProperty(--safe-right, safe.right px); } window.addEventListener(DOMContentLoaded, applySafeArea);.bottom-bar { padding-bottom: var(--safe-bottom, 0px); }4.3 全面屏與手勢導航Android 10 及以上系統(tǒng)默認啟用全面屏手勢導航底部手勢條區(qū)域同樣需要預留空間。建議在原生層將WindowInsetsCompat.Type.systemBars()與navigationBars()一并納入計算。5. 鴻蒙HarmonyOS安全區(qū)域適配鴻蒙系統(tǒng)在 ArkWeb 組件中提供了安全區(qū)域相關能力適配思路與 Android 類似但 API 名稱和注入方式有所不同。5.1 ArkWeb 安全區(qū)獲取在鴻蒙原生側(cè)可以通過onAreaChange或系統(tǒng)提供的安全區(qū)域接口獲取避讓區(qū)域信息再注入到 H5 頁面。// ArkTS 示例 this.webController.onAreaChange((oldValue, newValue) { const top newValue.topRect.height; const bottom newValue.bottomRect.height; this.webController.runJavaScript( window.safeArea { top: ${top}, bottom: ${bottom}, left: 0, right: 0 }; ); });5.2 前端統(tǒng)一適配方案鴻蒙端同樣建議通過注入window.safeArea的方式與 Android 共用同一套前端適配邏輯降低維護成本。function getSafeArea() { return window.safeArea || { top: 0, bottom: 0, left: 0, right: 0 }; }5.3 注意事項鴻蒙 WebView 對env(safe-area-inset-*)的支持尚不統(tǒng)一建議優(yōu)先使用原生注入方案。鴻蒙的避讓區(qū)域可能包含頂部狀態(tài)欄、底部導航條以及折疊屏的鉸鏈區(qū)域需要按實際設備驗證。6. 三端統(tǒng)一適配方案為了降低多端維護成本推薦采用「原生注入 CSS 變量 兜底值」的統(tǒng)一方案。6.1 統(tǒng)一前端邏輯const DEFAULT_SAFE { top: 0, bottom: 0, left: 0, right: 0 }; function initSafeArea() { const safe window.safeArea || DEFAULT_SAFE; const root document.documentElement; root.style.setProperty(--safe-top, safe.top px); root.style.setProperty(--safe-bottom, safe.bottom px); root.style.setProperty(--safe-left, safe.left px); root.style.setProperty(--safe-right, safe.right px); } // iOS 優(yōu)先使用 env() if (window.safeArea undefined) { const style document.documentElement.style; style.setProperty(--safe-top, env(safe-area-inset-top, 0px)); style.setProperty(--safe-bottom, env(safe-area-inset-bottom, 0px)); style.setProperty(--safe-left, env(safe-area-inset-left, 0px)); style.setProperty(--safe-right, env(safe-area-inset-right, 0px)); } initSafeArea();6.2 統(tǒng)一 CSS 使用方式.page-header { padding-top: var(--safe-top, 0px); } .page-footer { padding-bottom: var(--safe-bottom, 0px); }7. 常見問題與避坑指南iOS 未設置 viewport-fitcoverenv() 讀取不到安全區(qū)數(shù)值頁面內(nèi)容無法延伸到劉海區(qū)域。Android 機型差異不同廠商對劉海高度的上報不一致建議以真機實測為準。橫屏適配遺漏橫屏時左右兩側(cè)的 safe-area-inset-left/right 同樣需要處理。鍵盤彈出遮擋底部輸入框需要監(jiān)聽鍵盤高度動態(tài)調(diào)整布局不能只依賴安全區(qū)。鴻蒙版本差異不同 HarmonyOS 版本對安全區(qū) API 的支持有差異需要做好版本兼容。8. 總結(jié)安全區(qū)域適配是移動端 H5 開發(fā)的基礎能力三端適配的核心思路是一致的先獲取系統(tǒng)安全區(qū)數(shù)值再通過 CSS 變量或 env() 應用到頁面布局。iOS 依賴系統(tǒng)內(nèi)置的 env() 機制Android 和鴻蒙則更適合通過原生注入統(tǒng)一處理。建議在項目中封裝一套統(tǒng)一的安全區(qū)工具函數(shù)配合真機測試覆蓋主流機型才能保證頁面在各類全面屏設備上都有良好的體驗。