我正在使用 Jetpack Compose 开发 Android 应用程序,并尝试实现动态主题更改功能。主题是使用自定义 PlanstyTheme 函数设置的,用户可以从 ThemeSelectionScreen 中选择主题。主题选择应该立即更新应用程序的外观,但在 ThemeScreen 中切换主题后,更改似乎没有生效。
这是我的 MainActivity 的片段:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val preferences: AppPreferences by inject()
setContent {
val viewmodel: MainScreenModel by inject()
val isOnBoardingDone by preferences.isOnboardingComplete.collectAsState(initial = false)
val appTheme by viewmodel.appTheme.collectAsState(initial = AppTheme.SYSTEM)
val accentColor by viewmodel.accentColor.collectAsState(initial = MaterialTheme.colorScheme.primaryContainer)
Log.i("theme name Main", appTheme.name)
Log.i("accent theme name Main", "${accentColor.value}")
PlanstyTheme(appTheme = appTheme, accentColor = accentColor) {
if (isOnBoardingDone) {
Navigator(MainContainer)
} else {
Navigator(OnboardingScreen)
}
}
}
}
下面是我的主题可组合方法
@Composable
fun PlanstyTheme(
appTheme: AppTheme,
accentColor: Color,
content: @Composable () -> Unit
) {
val dark = appTheme == AppTheme.DARK || (appTheme == AppTheme.SYSTEM && isSystemInDarkTheme())
val colorScheme = when {
dark -> darkScheme.copy(primary = accentColor)
else -> lightScheme.copy(primary = accentColor)
}
CompositionLocalProvider(
localColorScheme provides colorScheme
) {
MaterialTheme(
colorScheme = colorScheme,
content = content
)
}
// Handle system UI colors
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = Color.Transparent.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !dark
}
}
我也会粘贴我的数据存储中的代码
class AppPreferences(private val context: Context) {
// to make sure there's only one instance
companion object {
private val Context.appPreference: DataStore<Preferences> by preferencesDataStore("preferences")
//keys
val ONBOARDING_KEY = booleanPreferencesKey("onboarding")
val POINTS_KEY = intPreferencesKey("points")
private val THEME_KEY = stringPreferencesKey("theme")
private val ACCENT_COLOR_KEY = intPreferencesKey("accent_color")
}
// themes and accents
suspend fun saveTheme(theme: AppTheme) {
context.appPreference.edit { preferences ->
preferences[THEME_KEY] = theme.name
}
Log.i("datastore theme","$theme")
}
fun getTheme() = context.appPreference.data.map { preferences ->
val theme = preferences[THEME_KEY] ?: AppTheme.SYSTEM.name
Log.i("get datastore theme","$theme")
AppTheme.valueOf(theme)
}
suspend fun saveAccentColor(color: Color) {
context.appPreference.edit { preferences ->
preferences[ACCENT_COLOR_KEY] = color.toArgb()
}
}
fun getAccentColor() = context.appPreference.data.map { preferences ->
preferences[ACCENT_COLOR_KEY]?.let { Color(it) } ?: Color.Gray
}
}
enum class AppTheme {
SYSTEM, LIGHT, DARK
}
还有其他代码,如果您需要查看我可以编辑并提供代码,我会将视图模型中的主题更新到数据存储
注意:我使用 voyager 进行导航和屏幕模型 我还注意到,当我改变主题时,状态栏会改变,但整个用户界面不会改变
根据要求,我将添加下面的 MainScreenModel 代码
class MainScreenModel(private val preferences: AppPreferences) : ScreenModel {
val appTheme = preferences.getTheme()
val accentColor = preferences.getAccentColor()}
当我关闭并重新打开应用程序时,主题会发生变化 如果您有任何建议,我真的很感激,我一整天都在这
datastore-preferences
的支持?
OnSharedPreferenceChangeListener
无法使用。在 Compose 中,您需要 MutableState
或 MutableStateFlow
。另请参阅:可组合项中的状态。
问题是,您使用
collectAsState()
分配主题,而应该是 mutableStateOf()
或 mutableStateListOf()
以便可观察并在值更改时触发重组。
val appTheme by viewmodel.appTheme.collectAsState(initial = AppTheme.SYSTEM)