在 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
}
}
七、注意事项
- 测试不同设备:尤其针对华为、小米、三星等厂商的刘海屏和挖孔屏设备。
- 键盘弹出处理 :使用
WindowInsetsCompat.Type.ime()
调整布局。 - 手势导航兼容:在 Android 10+ 中,避免隐藏导航栏导致手势冲突。
- 性能优化 :避免频繁修改
systemUiVisibility
或WindowInsetsController
。
通过上述步骤,可以实现在不同 Android 版本和设备上兼容的 Edge-to-Edge 沉浸式布局,同时确保系统栏颜色、安全区域和交互逻辑的正确性。