You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin开发:如何在应用关闭后仍保留用户设置偏好

Android Settings Fragment 偏好持久化实现示例

推荐使用Jetpack Preferences DataStore实现,相比旧版SharedPreferences,DataStore支持异步操作、事务处理、不会阻塞主线程,更适合持久化轻量配置。

1. 引入依赖

在模块级build.gradle中添加DataStore依赖:

dependencies {
    // Preferences DataStore
    implementation "androidx.datastore:datastore-preferences:1.0.0"
    // 协程基础依赖
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
}

2. 封装偏好管理工具类

创建单例工具类统一管理读写逻辑:

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

// 全局扩展方法初始化DataStore,存储文件名为app_settings
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "app_settings")

class SettingsPrefManager private constructor(private val context: Context) {
    // 定义存储字段的key
    private val SELECTED_LANGUAGE = stringPreferencesKey("selected_language")
    private val SELECTED_THEME = stringPreferencesKey("selected_theme")

    companion object {
        @Volatile
        private var INSTANCE: SettingsPrefManager? = null
        // 单例获取方法
        fun getInstance(context: Context): SettingsPrefManager {
            return INSTANCE ?: synchronized(this) {
                val instance = SettingsPrefManager(context.applicationContext)
                INSTANCE = instance
                instance
            }
        }
    }

    // 保存选中的语言编码
    suspend fun saveLanguage(langCode: String) {
        context.dataStore.edit { preferences ->
            preferences[SELECTED_LANGUAGE] = langCode
        }
    }

    // 读取选中的语言,默认返回简体中文
    val selectedLanguage: Flow<String> = context.dataStore.data
        .map { preferences ->
            preferences[SELECTED_LANGUAGE] ?: "zh-CN"
        }

    // 保存选中的主题模式
    suspend fun saveTheme(themeMode: String) {
        context.dataStore.edit { preferences ->
            preferences[SELECTED_THEME] = themeMode
        }
    }

    // 读取选中的主题,默认返回跟随系统
    val selectedTheme: Flow<String> = context.dataStore.data
        .map { preferences ->
            preferences[SELECTED_THEME] ?: "system"
        }
}

3. Settings Fragment中调用逻辑

在设置页面选择配置后触发保存,页面初始化时读取已有配置渲染UI:

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

class SettingsFragment : Fragment() {
    private lateinit var prefManager: SettingsPrefManager
    private lateinit var languageSpinner: Spinner
    private lateinit var themeSpinner: Spinner

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_settings, container, false)
        prefManager = SettingsPrefManager.getInstance(requireContext())
        languageSpinner = view.findViewById(R.id.spinner_language)
        themeSpinner = view.findViewById(R.id.spinner_theme)

        // 初始化下拉选择器的选项
        initSpinners()

        // 读取已保存的配置,设置选择器默认选中项
        lifecycleScope.launch {
            prefManager.selectedLanguage.collect { langCode ->
                val position = (languageSpinner.adapter as ArrayAdapter<String>).getPosition(langCode)
                languageSpinner.setSelection(position)
            }
        }

        lifecycleScope.launch {
            prefManager.selectedTheme.collect { themeMode ->
                val position = (themeSpinner.adapter as ArrayAdapter<String>).getPosition(themeMode)
                themeSpinner.setSelection(position)
            }
        }

        // 语言选择改变时保存配置
        languageSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val selectedLang = parent?.getItemAtPosition(position) as String
                lifecycleScope.launch {
                    prefManager.saveLanguage(selectedLang)
                    // 保存后重启Activity生效语言配置
                    requireActivity().recreate()
                }
            }
            override fun onNothingSelected(parent: AdapterView<*>?) = Unit
        }

        // 主题选择改变时保存配置
        themeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val selectedTheme = parent?.getItemAtPosition(position) as String
                lifecycleScope.launch {
                    prefManager.saveTheme(selectedTheme)
                    // 全局设置主题即时生效
                    when(selectedTheme) {
                        "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
                        "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
                        "system" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
                    }
                }
            }
            override fun onNothingSelected(parent: AdapterView<*>?) = Unit
        }

        return view
    }

    private fun initSpinners() {
        // 给下拉选择器填充可选选项,可根据业务调整
        val languageOptions = arrayOf("zh-CN", "en-US", "ja-JP")
        languageSpinner.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, languageOptions)

        val themeOptions = arrayOf("system", "light", "dark")
        themeSpinner.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, themeOptions)
    }
}

4. 应用启动时加载全局配置

自定义Application类,启动时读取配置直接全局生效,避免每次打开应用重新走选择逻辑:

import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import kotlinx.coroutines.runBlocking

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        val prefManager = SettingsPrefManager.getInstance(this)

        // 启动时读取主题配置直接生效
        runBlocking {
            val theme = prefManager.selectedTheme.first()
            when(theme) {
                "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
                "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
                "system" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
            }
        }

        // 多语言配置同理,此处读取存储的语言编码后设置应用全局Locale即可
    }
}

注意需要在AndroidManifest.xml的application标签中添加android:name=".MyApp"指定自定义Application类,否则配置不会生效。


内容的提问来源于stack exchange,提问作者Melom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 19:15:03