ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

H5 应用开发:Android、iOS 与鸿蒙安全区域适配实战

H5 应用开发:Android、iOS 与鸿蒙安全区域适配实战 1. 引言在移动端 H5 应用开发中安全区域Safe Area适配是绕不开的难题。随着全面屏、刘海屏、挖孔屏以及灵动岛的普及页面底部和顶部常常被系统手势条、状态栏或摄像头区域遮挡导致按钮不可点、文字被截断。本文围绕 Android、iOS 和鸿蒙HarmonyOS三大平台系统讲解安全区域适配的原理与最佳实践。2. 什么是安全区域安全区域是指系统为应用内容预留的、不会被状态栏、导航栏、手势条、圆角或刘海遮挡的可视区域。不同平台对安全区域的定义和获取方式各不相同但目标一致让关键内容始终完整可见、可交互。常见需要适配的场景包括顶部状态栏与刘海区域遮挡标题栏。底部手势条Home Indicator遮挡操作按钮。横屏时左右两侧的圆角与刘海区域。键盘弹出时底部输入框被遮挡。3. iOS 安全区域适配iOS 从 iPhone X 开始引入刘海屏系统通过safe-area-inset-*环境变量向 WebView 暴露安全区域信息适配最为规范。3.1 viewport 设置在 HTML 的 viewport meta 标签中必须设置viewport-fitcover否则页面默认不会延伸到安全区域之外也就无法读取安全区数值。meta nameviewport contentwidthdevice-width, initial-scale1.0, viewport-fitcover /3.2 CSS env() 函数设置 viewport-fitcover 后即可通过 CSS 的env()函数读取四个方向的安全区 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 底部固定按钮适配底部固定操作栏是最常见的适配场景需要同时考虑手势条高度和底部安全区。.bottom-bar { position: fixed; left: 0; right: 0; bottom: 0; padding-bottom: env(safe-area-inset-bottom); background-color: #ffffff; }4. Android 安全区域适配Android 生态碎片化严重不同厂商对刘海屏、挖孔屏的处理方式不同WebView 对安全区域的暴露也不如 iOS 统一。Android 端通常需要结合原生代码与 CSS 变量共同处理。4.1 原生侧获取安全区在 Android 原生层可以通过WindowInsets获取系统栏和安全区域信息再通过 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动态设置 CSS 变量实现安全区适配。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 及以上系统默认启用全面屏手势导航底部手势条区域同样需要预留空间。建议在原生层将WindowInsetsCompat.Type.systemBars()与navigationBars()一并纳入计算。5. 鸿蒙HarmonyOS安全区域适配鸿蒙系统在 ArkWeb 组件中提供了安全区域相关能力适配思路与 Android 类似但 API 名称和注入方式有所不同。5.1 ArkWeb 安全区获取在鸿蒙原生侧可以通过onAreaChange或系统提供的安全区域接口获取避让区域信息再注入到 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 前端统一适配方案鸿蒙端同样建议通过注入window.safeArea的方式与 Android 共用同一套前端适配逻辑降低维护成本。function getSafeArea() { return window.safeArea || { top: 0, bottom: 0, left: 0, right: 0 }; }5.3 注意事项鸿蒙 WebView 对env(safe-area-inset-*)的支持尚不统一建议优先使用原生注入方案。鸿蒙的避让区域可能包含顶部状态栏、底部导航条以及折叠屏的铰链区域需要按实际设备验证。6. 三端统一适配方案为了降低多端维护成本推荐采用「原生注入 CSS 变量 兜底值」的统一方案。6.1 统一前端逻辑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 优先使用 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 统一 CSS 使用方式.page-header { padding-top: var(--safe-top, 0px); } .page-footer { padding-bottom: var(--safe-bottom, 0px); }7. 常见问题与避坑指南iOS 未设置 viewport-fitcoverenv() 读取不到安全区数值页面内容无法延伸到刘海区域。Android 机型差异不同厂商对刘海高度的上报不一致建议以真机实测为准。横屏适配遗漏横屏时左右两侧的 safe-area-inset-left/right 同样需要处理。键盘弹出遮挡底部输入框需要监听键盘高度动态调整布局不能只依赖安全区。鸿蒙版本差异不同 HarmonyOS 版本对安全区 API 的支持有差异需要做好版本兼容。8. 总结安全区域适配是移动端 H5 开发的基础能力三端适配的核心思路是一致的先获取系统安全区数值再通过 CSS 变量或 env() 应用到页面布局。iOS 依赖系统内置的 env() 机制Android 和鸿蒙则更适合通过原生注入统一处理。建议在项目中封装一套统一的安全区工具函数配合真机测试覆盖主流机型才能保证页面在各类全面屏设备上都有良好的体验。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进