前端网页在android 沉浸式模式 处理

66 阅读3分钟

说明

  • 沉浸式(Immersive):状态栏/导航栏隐藏(或半隐),网页内容可延伸到屏幕四边/刘海/圆角区。适合做全屏体验,但网页需要自己做“安全区域”内边距处理。
  • 非沉浸式:系统栏可见,应用内容布局在系统栏之下。默认更安全,网页通常无需特殊处理。

原理

  • 沉浸式:Android 让 App 内容“铺满屏幕”,系统栏通过手势临时唤出。WebView 里的网页如果不处理,会“顶到”状态栏或被手势区遮挡。
  • 非沉浸式:系统把可用区域“裁好”再交给 App,WebView 正常渲染,不会被遮挡。

android原生设置

// 开启沉浸式(推荐 sticky,允许滑动临时唤出系统栏)
WindowCompat.setDecorFitsSystemWindows(window, false)
val controller = WindowInsetsControllerCompat(window, window.decorView)
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsetsCompat.Type.systemBars())

// 关闭沉浸式(非沉浸式)
WindowCompat.setDecorFitsSystemWindows(window, true)
WindowInsetsControllerCompat(window, window.decorView)
  .show(WindowInsetsCompat.Type.systemBars())

webview处理

A) 原生开了“沉浸式”

  1. HTML 头部加 viewport(允许内容延伸到屏幕边缘/刘海区)
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  1. 使用安全区变量为页面或固定头/尾添加内边距(Chrome/Android、iOS 都兼容,未全屏时值为 0)
/* 页面整体安全区 */

html, body {

  height100%;

  /* 兼容动态地址栏推荐用 dvh/svh,WebView 通常稳定,但写上更稳 */

  min-height: 100dvh;

  padding-topenv(safe-area-inset-top);

  padding-bottomenv(safe-area-inset-bottom);

  background: #fff; /* 避免系统栏下白闪 */

}

/* 有固定头部时,避免被状态栏/刘海压住 */

.header-fixed {

  position: fixed;

  top0;

  left0right0;

  padding-topenv(safe-area-inset-top);

}

/* 有底部 Tab/操作条时,避免被手势区遮挡 */

.footer-fixed {

  position: fixed;

  bottom0;

  left0right0;

  padding-bottomenv(safe-area-inset-bottom);

}
  1. 可选:原生把状态栏高度/Insets同步给网页,做更细控制
// Kotlin:测量 insets 并注入 CSS 变量

val insets = ViewCompat.getRootWindowInsets(window.decorView)

val top = insets?.getInsets(WindowInsetsCompat.Type.statusBars())?.top ?: 0

webView.evaluateJavascript(

  "document.documentElement.style.setProperty('--status-bar-height', '${top}px')",

  null

)

/* 网页使用注入的变量(与 env 并用,谁有值用谁) */

.header-fixed {

  padding-topmax(env(safe-area-inset-top), var(--status-bar-height, 0px));

}

B) 原生是“非沉浸式”

  • 通常无需额外适配,WebView 可用区域已避开系统栏。
  • 仍建议使用现代视窗单位,避免 100vh 在移动端的历史问题:

    .app {

      min-height: 100dvh;  /* 优先 */

      /* 回退 */

      min-height: 100vh;

    }

常见坑与建议

  • 不要写死 20/24px 当状态栏高度:刘海/圆角设备会不准,统一用 env(safe-area-inset-*) 或注入变量。
  • 固定头/尾必须处理安全区:尤其沉浸式下的 position: fixed 元素。
  • 背景色要填满:body/html 设置背景,避免系统栏区域露出黑/白。
  • Keyboard/输入法:表单页面尽量避免将输入框固定在最底部;如需固定,结合 visualViewport 或在原生侧开启 adjustResize。

一套通用最小改动方案(同时兼容两种模式)

  • 原生:需要沉浸式时用 decorFitsSystemWindows(false) + hide(systemBars);不需要就开 true。
  • 网页:统一加 viewport-fit=cover,全局/头/尾使用 env(safe-area-inset-*) 做内边距;页面容器用 min-height: 100dvh。
  • 如需像素级一致:原生注入 --status-bar-height 作为兜底,CSS 用 max(env(...), var(--...))。

capacitor设置

import { Capacitor } from '@capacitor/core';
import { StatusBar, Style } from '@capacitor/status-bar';
 const canUseStatusBar = Capacitor.isPluginAvailable?.('StatusBar')
 if(!canUseStatusBar) return
 await StatusBar.setOverlaysWebView({ overlay: true });
 await StatusBar.setBackgroundColor({ color: '#008F91FF' });
 await StatusBar.setStyle({ style: Style.Light });