Android Kotlin开发如何在夜间模式基础上实现AMOLED主题
Android Kotlin 实现AMOLED纯黑主题规范方案
基于你现有的主题切换框架,只需要做少量改造即可新增AMOLED主题选项,不需要重构原有逻辑。
1. 新增AMOLED主题资源
首先在主题资源文件中新增AMOLED专属样式,核心是将界面背景、系统栏设置为纯黑,控件层级用深灰区分即可:
<!-- res/values-night/themes.xml 内新增 --> <style name="Theme.YourApp.Amoled" parent="Theme.Material3.DayNight.NoActionBar"> <!-- 核心纯黑背景 --> <item name="android:windowBackground">@android:color/black</item> <item name="colorSurface">@android:color/black</item> <item name="colorOnSurface">@color/white</item> <!-- 适配edge-to-edge的系统栏纯黑设置 --> <item name="android:statusBarColor">@android:color/black</item> <item name="android:navigationBarColor">@android:color/black</item> <!-- 卡片、弹窗等层级控件建议用#121212这类深灰,和纯黑背景做出视觉区分 --> <item name="colorSurfaceVariant">@color/deep_gray</item> </style>
在设置页的主题选择列表中,新增一个选项值nightModeAmoled即可。
2. 重构ThemeHelper逻辑
你原有代码的核心问题是主题应用时机不对、硬编码太多,改造后统一处理四种主题状态(跟随系统、亮色、普通暗色、AMOLED纯黑):
import android.graphics.Color import android.util.TypedValue object ThemeHelper { // 统一管理主题常量,避免硬编码拼写错误 const val THEME_FOLLOW_SYSTEM = "nightModeFollowSystem" const val THEME_LIGHT = "nightModeOff" const val THEME_DARK = "nightModeOn" const val THEME_AMOLED = "nightModeAmoled" private fun isNightMode(context: Context): Boolean { return context.resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES } fun getSelectedTheme(context: Context): String { val prefs = PreferenceManager.getDefaultSharedPreferences(context) return prefs.getString("modTema", THEME_FOLLOW_SYSTEM) ?: THEME_FOLLOW_SYSTEM } /** * 必须在Activity的super.onCreate调用前执行 */ fun applyTheme(activity: Activity) { when(getSelectedTheme(activity)) { THEME_LIGHT -> { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) activity.setTheme(R.style.Theme_YourApp) } THEME_DARK -> { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) activity.setTheme(R.style.Theme_YourApp) } THEME_AMOLED -> { // AMOLED属于暗色模式分支,先开启夜间模式再加载纯黑主题 AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) activity.setTheme(R.style.Theme_YourApp_Amoled) } THEME_FOLLOW_SYSTEM -> { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) activity.setTheme(R.style.Theme_YourApp) } } } fun recreateIfNeeded(activity: Activity) { val currentSelected = getSelectedTheme(activity) val currentIsNight = isNightMode(activity) var needRecreate = true // 匹配当前状态和选中主题,完全一致则不重建 when { currentSelected == THEME_LIGHT && !currentIsNight -> needRecreate = false currentSelected == THEME_DARK && currentIsNight -> needRecreate = false currentSelected == THEME_AMOLED && currentIsNight && isCurrentAmoled(activity) -> needRecreate = false currentSelected == THEME_FOLLOW_SYSTEM -> { val systemIsNight = (activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES needRecreate = currentIsNight != systemIsNight } } if (needRecreate) { val intent = activity.intent.apply { putExtra("TEMA_CAMBIATO", true) } activity.finish() activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) activity.startActivity(intent) } } // 校验当前是否已经加载AMOLED主题 private fun isCurrentAmoled(activity: Activity): Boolean { val typedValue = TypedValue() activity.theme.resolveAttribute(android.R.attr.windowBackground, typedValue, true) return typedValue.data == Color.BLACK } }
3. 修正MainActivity主题应用时机
你原来把setDefaultNightMode放在super.onCreate之后是不符合官方规范的,会触发重复重建,调整到super.onCreate之前调用即可:
override fun onCreate(savedInstanceState: Bundle?) { // 先应用主题,再执行其他初始化 ThemeHelper.applyTheme(this) WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) // 原有setContentView、导航初始化等逻辑保留 } override fun onResume() { super.onResume() if (intent.extras?.getBoolean("TEMA_CAMBIATO") == true) { intent.putExtra("TEMA_CAMBIATO", false) navController.navigate(R.id.SettingsFragment) } }
4. 调整设置页监听逻辑
把原有重建方法替换成新的判断方法即可:
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (isAdded && key == "modTema") { ThemeHelper.recreateIfNeeded(requireActivity()) } }
额外适配提示
- 如果使用Material组件,确保主题parent继承自对应版本的Material主题,否则控件颜色不会自动适配
- AMOLED主题下不要所有元素都用纯黑,卡片、弹窗、底部栏用深灰色做层级区分,避免界面没有层次
- 如果适配了动态取色,记得在AMOLED主题中覆盖surface相关颜色为纯黑即可
内容的提问来源于stack exchange,提问作者Mardev
相关产品推荐
相关产品推荐

