Android 使用 Edge-to-Edge 实现沉浸式状态栏详解

在 Android 中实现 Edge-to-Edge 布局(内容延伸到状态栏和导航栏下方)并适配不同版本、刘海屏/挖孔屏设备,需要综合处理系统栏的显示、颜色、安全区域和兼容性问题。以下是分步骤的完整实现方案:

一、基础配置:启用 Edge-to-Edge

1. 设置透明系统栏

styles.xml 或代码中移除系统栏的默认半透明遮罩:

xml 复制代码
<!-- styles.xml -->
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="android:windowTranslucentStatus">false</item>
    <item name="android:windowTranslucentNavigation">false</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:navigationBarColor">@android:color/transparent</item>
</style>
kotlin 复制代码
// Activity 代码中设置
window.apply {
    statusBarColor = Color.TRANSPARENT
    navigationBarColor = Color.TRANSPARENT
    // 允许内容绘制到系统栏下方
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        isStatusBarContrastEnforced = false
        //Android 10以后会存在遮罩,false去掉这个遮罩
        isNavigationBarContrastEnforced = false
    }
}

2. 允许内容延伸到系统栏区域

onCreate 中设置 Window 标志:

kotlin 复制代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    window.setDecorFitsSystemWindows(false)
} else {
    @Suppress("DEPRECATION")
    window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
}

二、处理系统栏的显示与隐藏

1. 隐藏系统栏(全屏模式)

kotlin 复制代码
private fun hideSystemBars() {
    val decorView = window.decorView
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.insetsController?.hide(WindowInsets.Type.systemBars())
    } else {
        @Suppress("DEPRECATION")
        decorView.systemUiVisibility = (
            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
            View.SYSTEM_UI_FLAG_FULLSCREEN
        )
    }
}

2. 显示系统栏

kotlin 复制代码
private fun showSystemBars() {
    val decorView = window.decorView
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.insetsController?.show(WindowInsets.Type.systemBars())
    } else {
        @Suppress("DEPRECATION")
        decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    }
}

三、修改系统栏颜色和前景色

1. 设置背景色

kotlin 复制代码
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT

2. 设置图标/文字颜色(深色或浅色)

kotlin 复制代码
// 状态栏图标颜色(Android 6.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    val isLight = isSystemBarIconLight() // 根据背景色动态判断
    val visibility = if (isLight) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else 0
    window.decorView.systemUiVisibility = visibility
}

// 导航栏图标颜色(Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val isLight = isSystemBarIconLight() // 根据背景色动态判断
    window.decorView.systemUiVisibility = if (isLight) {
        window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
    } else {
        window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
    }
}

// Android 11+ 使用 WindowInsetsControllerCompat
val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView)
windowInsetsController?.isAppearanceLightStatusBars = isLight
windowInsetsController?.isAppearanceLightNavigationBars = isLight

四、处理安全区域(Insets)

1. 适配刘海屏/挖孔屏

AndroidManifest.xml 中声明支持刘海屏:

xml 复制代码
<application
    ...
    android:resizeableActivity="true">
    <meta-data
        android:name="android.max_aspect"
        android:value="2.1" />
    <meta-data
        android:name="android.notch_support"
        android:value="true" />
</application>

在代码中设置布局模式:

kotlin 复制代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    window.attributes.layoutInDisplayCutoutMode =
        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}

2. 处理系统栏遮挡内容

使用 WindowInsetsCompat 动态调整布局:

kotlin 复制代码
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets ->
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    view.updatePadding(
        top = systemBars.top, // 状态栏高度
        bottom = systemBars.bottom // 导航栏高度
    )
    insets
}

五、兼容性处理

1. 版本兼容方案

kotlin 复制代码
fun handleEdgeToEdge(window: Window, rootView: View) {
    // 设置透明系统栏
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        window.statusBarColor = Color.TRANSPARENT
        window.navigationBarColor = Color.TRANSPARENT
    }

    // 处理系统栏 Insets(处理安全边衬区)
    ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
        val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
        view.updatePadding(
            top = systemBars.top,
            bottom = systemBars.bottom
        )
        insets
    }

    // 适配刘海屏(API 28+)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        window.attributes.layoutInDisplayCutoutMode =
            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    }
}

2. 厂商特定适配

  • 华为刘海屏 :通过 HwNotchSizeUtil 获取刘海尺寸。
  • 小米挖孔屏 :读取 ro.miui.notch 属性判断是否支持。

六、完整示例

kotlin 复制代码
class EdgeToEdgeActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_edge_to_edge)

        // 1. 启用 Edge-to-Edge
        enableEdgeToEdge()

        // 2. 处理系统栏 Insets
        handleSystemInsets()

        // 3. 动态修改系统栏颜色
        updateSystemBarColors(isDarkTheme = true)
    }

    private fun enableEdgeToEdge() {
        window.apply {
            statusBarColor = Color.TRANSPARENT
            navigationBarColor = Color.TRANSPARENT
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                setDecorFitsSystemWindows(false)
            } else {
                @Suppress("DEPRECATION")
                decorView.systemUiFlags = (
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                )
            }
        }
    }

    private fun handleSystemInsets() {
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { view, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            view.updatePadding(
                top = systemBars.top,
                bottom = systemBars.bottom
            )
            insets
        }
    }

    private fun updateSystemBarColors(isDarkTheme: Boolean) {
        val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView)
        windowInsetsController?.isAppearanceLightStatusBars = !isDarkTheme
        windowInsetsController?.isAppearanceLightNavigationBars = !isDarkTheme
    }
}

七、注意事项

  1. 测试不同设备:尤其针对华为、小米、三星等厂商的刘海屏和挖孔屏设备。
  2. 键盘弹出处理 :使用 WindowInsetsCompat.Type.ime() 调整布局。
  3. 手势导航兼容:在 Android 10+ 中,避免隐藏导航栏导致手势冲突。
  4. 性能优化 :避免频繁修改 systemUiVisibilityWindowInsetsController

通过上述步骤,可以实现在不同 Android 版本和设备上兼容的 Edge-to-Edge 沉浸式布局,同时确保系统栏颜色、安全区域和交互逻辑的正确性。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  4. Android 详解:高频使用的 8 种设计模式的核心思想和代码实现
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
相关推荐
BD_Marathon7 小时前
【MySQL】函数
android·数据库·mysql
西西学代码7 小时前
安卓开发---耳机的按键设置的UI实例
android·ui
maki07712 小时前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架12 小时前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid15 小时前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl16 小时前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea
旷野说18 小时前
Android Studio Narwhal 3 特性
android·ide·android studio
maki0771 天前
VR大空间资料 01 —— 常用VR框架对比
android·ue5·游戏引擎·vr·虚幻·pico
xhBruce1 天前
InputReader与InputDispatcher关系 - android-15.0.0_r23
android·ims
领创工作室1 天前
安卓设备分区作用详解-测试机红米K40
android·java·linux