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. 性能优化 :避免频繁修改 systemUiVisibility 或 WindowInsetsController。

通过上述步骤,可以实现在不同 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 的区别和用法
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work3 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work3 天前
ch21 签名、校验与发版
kotlin
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui