Kotlin系统做深色设定

xzbxzb 安卓 2025-12-06 7 0
以下是完整的实现代码,包含 Switch 切换(id: darkswichset)跟随系统 / 永久亮色切换动态生效 和 持久化保存 所有核心逻辑,代码可直接复制使用:

一、核心工具类(DarkModeManager.kt)

封装 SharedPreferences 持久化逻辑,统一管理深色模式配置:
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)

App 启动时自动恢复保存的深色模式设置:
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)

包含 id 为 darkswichset 的 Switch 控件:
<?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)

实现 Switch 切换、动态生效、持久化保存:
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)

注册自定义 Application 并配置 AppCompat 主题:
<?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>

六、关键说明

  1. Switch 逻辑

    • 勾选(isChecked = true)→ 深色模式跟随系统

    • 取消勾选(isChecked = false)→ 强制永久亮色(关闭深色模式)

  1. 主题要求

    • 必须使用 Theme.AppCompat.DayNight 或其子主题(如 Theme.MaterialComponents.DayNight),否则切换不生效。

    • 避免使用 Theme.AppCompat.Light(纯亮色)或 Theme.AppCompat(默认浅色),虽然也能生效,但 DayNight 主题兼容性更好。

  1. 动态生效

    • recreate() 会重建当前 Activity,若需全局生效(如所有页面),建议使用「单 Activity + Fragment」架构,仅重建 Fragment 即可。

  1. 兼容性

    • 支持 Android API 14+(AppCompat 库最低兼容版本),覆盖绝大多数设备。

  1. 持久化

    • 模式保存在 SharedPreferences 中,App 重启后不会丢失设置。

七、扩展优化(可选)

如果需要避免 recreate() 导致的页面闪烁,可采用以下方案:
  1. 使用 Activity.recreate() 的替代方案:重启 Activity 栈(适合多 Activity 场景);

  2. 采用 Jetpack Compose 开发 UI(Compose 支持更顺滑的主题切换,无需重建 Activity);

  3. 延迟重建:在用户确认后再重建,而非实时切换。

以上代码可直接集成到项目中,只需替换包名、图标等个性化配置即可运行。


 您阅读本篇文章共花了: 

版权声明

本文章如果涉及侵权,请联系我。
部分文章系本人原创未经许可,不得转载。

喜欢0发布评论

评论列表

发表评论

  • 昵称(必填)
  • 邮箱
  • 网址