一、核心工具类(DarkModeManager.kt)
import android.content.Context
import androidx.appcompat.app.AppCompatDelegate
/**
* 深色模式管理工具类:仅支持「跟随系统」和「永久亮色」两种模式
*/
object DarkModeManager {
// SP 存储文件名
private const val PREF_NAME = "dark_mode_settings"
// 存储键名
private const val KEY_DARK_MODE = "dark_mode_status"
/**
* 保存深色模式设置
* @param context 上下文
* @param mode 模式值:MODE_NIGHT_FOLLOW_SYSTEM(跟随系统)/ MODE_NIGHT_NO(永久亮色)
*/
fun saveDarkMode(context: Context, mode: Int) {
val sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
sp.edit().putInt(KEY_DARK_MODE, mode).apply()
}
/**
* 获取保存的深色模式(默认:跟随系统)
*/
fun getSavedDarkMode(context: Context): Int {
val sp = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
return sp.getInt(KEY_DARK_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
/**
* 应用深色模式(全局生效)
*/
fun applyDarkMode(context: Context, mode: Int) {
AppCompatDelegate.setDefaultNightMode(mode)
saveDarkMode(context, mode)
}
/**
* 判断当前是否是「跟随系统」模式
*/
fun isFollowSystem(context: Context): Boolean {
return getSavedDarkMode(context) == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
}二、自定义 Application(MyApp.kt)
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// 读取本地保存的模式,初始化全局深色模式
val savedMode = DarkModeManager.getSavedDarkMode(this)
AppCompatDelegate.setDefaultNightMode(savedMode)
}
}三、设置页面布局(activity_settings.xml)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="20dp"> <!-- 深色模式切换项 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:alignItems="center" android:orientation="horizontal" android:paddingVertical="12dp"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="深色模式" android:textSize="18sp" /> <Switch android:id="@+id/darkswichset" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="跟随系统" /> <!-- Switch 文本说明:开启=跟随系统,关闭=永久亮色 --> </LinearLayout> </LinearLayout>
四、设置页面逻辑(SettingsActivity.kt)
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
class SettingsActivity : AppCompatActivity() {
private lateinit var darkSwitch: Switch
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
// 绑定 Switch 控件
darkSwitch = findViewById(R.id.darkswichset)
// 初始化 Switch 状态:开启=跟随系统,关闭=永久亮色
darkSwitch.isChecked = DarkModeManager.isFollowSystem(this)
// 监听 Switch 切换事件
darkSwitch.setOnCheckedChangeListener { _, isChecked ->
val targetMode = if (isChecked) {
// 开启:跟随系统
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
} else {
// 关闭:永久亮色
AppCompatDelegate.MODE_NIGHT_NO
}
// 应用模式 + 持久化保存
DarkModeManager.applyDarkMode(this, targetMode)
// 重建 Activity 使主题生效(关键)
recreate()
}
}
/**
* 可选:保存 Activity 重建时的临时数据(如输入框内容)
*/
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// 保存 Switch 状态(示例)
outState.putBoolean("dark_switch_state", darkSwitch.isChecked)
}
/**
* 可选:恢复 Activity 重建时的临时数据
*/
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
darkSwitch.isChecked = savedInstanceState.getBoolean("dark_switch_state")
}
}五、清单文件配置(AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.your.package.name"> <!-- 替换为你的包名 --> <application android:name=".MyApp" <!-- 关联自定义 Application --> android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.AppCompat.DayNight"> <!-- 核心:支持日夜切换的主题 --> <!-- 注册设置页面 Activity --> <activity android:name=".SettingsActivity" android:exported="false" /> <!-- 主 Activity(根据你的项目修改) --> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
六、关键说明
Switch 逻辑:
勾选(isChecked = true)→ 深色模式跟随系统
取消勾选(isChecked = false)→ 强制永久亮色(关闭深色模式)
主题要求:
必须使用 Theme.AppCompat.DayNight 或其子主题(如 Theme.MaterialComponents.DayNight),否则切换不生效。
避免使用 Theme.AppCompat.Light(纯亮色)或 Theme.AppCompat(默认浅色),虽然也能生效,但 DayNight 主题兼容性更好。
动态生效:
recreate() 会重建当前 Activity,若需全局生效(如所有页面),建议使用「单 Activity + Fragment」架构,仅重建 Fragment 即可。
兼容性:
支持 Android API 14+(AppCompat 库最低兼容版本),覆盖绝大多数设备。
持久化:
模式保存在 SharedPreferences 中,App 重启后不会丢失设置。
七、扩展优化(可选)
使用 Activity.recreate() 的替代方案:重启 Activity 栈(适合多 Activity 场景);
采用 Jetpack Compose 开发 UI(Compose 支持更顺滑的主题切换,无需重建 Activity);
延迟重建:在用户确认后再重建,而非实时切换。
版权声明
本文章如果涉及侵权,请联系我。
部分文章系本人原创未经许可,不得转载。



蒙公网安备 15090202000037号
评论列表
发表评论