PAYATHON 2026

如何在 Kotlin Multiplatform 中强制让 Samsung 状态栏透明?

排障赵师傅

通常不需要为 Samsung 单独添加一套“强制透明”代码。现有代码在 Android 14 上已经请求透明状态栏,并关闭了状态栏对比度保护。仅凭这些片段,还无法确定黑条的原因。

建议先检查 Samsung 对该应用的“相机开孔”设置,再确认页面背景是否绘制到了状态栏后方。代码上可以让 enableEdgeToEdge() 管理窗口配置,SystemAppearance() 只调整系统栏图标颜色。

先检查 Samsung 的“相机开孔”设置

在出现问题的手机上,打开:

设置 → 显示 → 相机开孔(Camera cutout)→ 选择你的应用

如果当前选中“隐藏相机开孔”,改为允许显示开孔的选项,然后重新打开应用。具体选项名称可能因 One UI 版本和机型而异。

Samsung 官方说明,“隐藏相机开孔”会用黑条覆盖开孔区域,且支持按应用设置。因此,即使其他应用的状态栏透明,你的应用也可能启用了这个选项。可以优先检查这一项,但目前还不能确定它就是这台设备出现黑条的原因。Samsung 官方说明

LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 用于请求窗口在相应开孔区域布局,不能保证覆盖 Samsung 的用户设置。

现有代码中有哪些问题?

Android 14 的透明配置已经存在

在 Android 14 上,以下两个设置都会执行:

window.statusBarColor = Color.Transparent.toArgb()
window.isStatusBarContrastEnforced = false

前者请求透明背景,后者关闭系统为状态栏自动添加的对比度保护。如果后续没有代码覆盖这两个设置,重复设置相同的值通常无法解释或解决黑条问题。Window API 文档

Android 9 及以下的分支则把状态栏颜色设为 primaryContainer,没有请求透明状态栏。这个颜色可能恰好与页面顶部一致,所以看起来像透明。

图标颜色和背景透明度是两个设置

controller.isAppearanceLightStatusBars = false

这行代码会使用浅色状态栏图标,不会把状态栏背景设成黑色。设为 true 时使用深色图标,适合浅色背景。Android 官方示例

SystemBarStyle.dark(Color.TRANSPARENT) 同样表示使用适合深色背景的系统栏样式,传入的背景色仍是透明色。

简化窗口配置

保留 MainActivity 中的 enableEdgeToEdge(),让 AndroidX 处理不同 Android 版本的窗口配置:

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()

        setContent {
            SongBookTheme {
                // 应用页面
            }
        }
    }
}

enableEdgeToEdge() 默认会配置透明状态栏。在三键导航模式下,底部导航栏默认可能带有半透明遮罩,这与顶部状态栏是否透明是两件事。Android edge-to-edge 文档

再将 Android 源集中的 SystemAppearance() 简化为只设置图标颜色:

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

private tailrec fun Context.findActivity(): Activity? =
    when (this) {
        is Activity -> this
        is ContextWrapper -> baseContext.findActivity()
        else -> null
    }

@Composable
actual fun SystemAppearance(isDark: Boolean) {
    val view = LocalView.current
    if (view.isInEditMode) return

    val activity = view.context.findActivity() ?: return

    SideEffect {
        val controller = WindowCompat.getInsetsController(
            activity.window,
            activity.window.decorView
        )

        controller.isAppearanceLightStatusBars = !isDark
        controller.isAppearanceLightNavigationBars = !isDark
    }
}

原有的 expect/actual 结构保持不变,SongBookTheme() 中调用 SystemAppearance(darkTheme) 的位置也可以保留。

使用 !isDark 的前提是状态栏后方的背景跟随应用的明暗主题。如果顶部显示照片,或工具栏始终为深色,应根据实际背景选择图标颜色。

确保背景确实延伸到状态栏后方

透明状态栏会显示后面的内容。如果页面背景从状态栏下方才开始绘制,上方仍可能露出黑色的窗口背景。

可以在 SongBookTheme() 的 MaterialTheme 内容块中,让背景铺满根布局,再给内部内容添加安全区域留白:

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier

// 放在现有 MaterialTheme(...) { ... } 内
SystemAppearance(darkTheme)

Box(
    modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colorScheme.background)
) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
    ) {
        content()
    }
}

外层背景可以绘制到系统栏后方,内部内容则避开系统栏和开孔区域。Insets 需要根据页面结构处理。如果 Scaffold 或顶部栏已经提供了相应留白,就不要重复叠加。Compose Insets 文档

排查时,可以临时把外层背景改成醒目的颜色:

.background(androidx.compose.ui.graphics.Color.Magenta)

如果状态栏区域随之变色,说明透明已经生效,可以继续检查原来的背景和 Insets 布局。如果仍然显示黑条,再检查 Samsung 的开孔设置,以及后续是否有页面、主题或第三方库重新修改了窗口配置。这一步只能缩小排查范围,不能单独确定具体原因。

版本与验证注意事项

这些改动属于 Kotlin Multiplatform 的 Android 平台实现,放在 Android 源集即可。

Window.setStatusBarColor() 从 API 35 起已弃用。后续维护应使用 edge-to-edge,并处理好状态栏后方的背景绘制,避免继续叠加旧的系统栏配置。API 文档

上述代码用于整理配置和排查问题,尚未在题述 Galaxy S23 Ultra 上验证。如果关闭“隐藏相机开孔”后黑条消失,就无需再增加 Samsung 专用代码;如果仍然存在,需要结合实际页面布局、One UI 版本和窗口配置继续排查。

备注:内容仅供参考。