Jetpack Compose:如何以编程方式 onClick 将主题从浅色模式更改为深色模式

Nic*_*age 18 android android-theme kotlin android-jetpack android-jetpack-compose

TL;DR 更改主题并在单击时在浅色和深色主题之间重新组合应用程序。

你好!我有一个有趣的问题,我一直在努力解决,希望得到一些帮助。我正在尝试实现一个设置屏幕,让用户更改应用程序的主题(选择与系统设置匹配的深色、浅色或自动)。

我在选择调色板时通过调用isSystemInDarkTheme()函数成功地动态设置了主题,但我很难通过单击按钮在浅色和深色主题之间重新组合应用程序。

我现在的策略是创建一个主题模型,该模型从用户实际在其中选择主题的设置组件中提升状态。然后,该主题模型将主题状态变量公开给自定义主题(围绕材质主题),以决定是否选择浅色或深色调色板。这是相关代码-->

主题

@Composable
fun CustomTheme(
themeViewModel: ThemeViewModel = viewModel(),
content: @Composable() () -> Unit,
) {
   val colors = when (themeViewModel.theme.value.toString()) {
       "Dark" -> DarkColorPalette
       "Light" -> LightColorPalette
       else -> if (isSystemInDarkTheme()) DarkColorPalette else LightColorPalette
   }

   MaterialTheme(
       colors = colors,
       typography = typography,
       shapes = shapes,
       content = content
   )
   }
Run Code Online (Sandbox Code Playgroud)

主题模型和状态变量

class ThemeViewModel : ViewModel() {
private val _theme = MutableLiveData("Auto")
val theme: LiveData<String> = _theme

fun onThemeChanged(newTheme: String) {
    when (newTheme) {
        "Auto" -> _theme.value = "Light"
        "Light" -> _theme.value = "Dark"
        "Dark" -> _theme.value = "Auto"
    }
}
}
Run Code Online (Sandbox Code Playgroud)

组件(UI)代码

@Composable
fun Settings(
   themeViewModel: ThemeViewModel = viewModel(),
) {
   ...
   val theme: String by themeViewModel.theme.observeAsState("")
   ...
   ScrollableColumn(Modifier.fillMaxSize()) {
       Column {
        ...
        Card() {
            Row() {
                Text(text = theme,
                    modifier = Modifier.clickable(
                        onClick = {
                            themeViewModel.onThemeChanged(theme)
                        }
                    )
                )
            }
        }
   }
Run Code Online (Sandbox Code Playgroud)

非常感谢您的时间和帮助!***我在 UI 组件中省略了一些代码,我可能在此过程中遗漏了一些闭包语法。

jns*_*jns 19

Jetpack 主题代码实验室中显示的一种可能性是通过输入参数设置暗模式,这确保在参数更改时主题将被重新组合:

@Composable
fun CustomTheme(
  darkTheme: Boolean = isSystemInDarkTheme(),
  content: @Composable () -> Unit
) {
  MaterialTheme(
    colors = if (darkTheme) DarkColors else LightColors,
    content = content
  )
}
Run Code Online (Sandbox Code Playgroud)

在你的 mainActivity 中,你可以观察 viewModel 的变化并将它们传递给你的 customTheme:

val darkTheme = themeViewModel.darkTheme.observeAsState(initial = true)

CustomTheme(darkTheme.value){
//yourContent
}
Run Code Online (Sandbox Code Playgroud)

这样,您的撰写预览就可以简单地采用深色主题样式:

@Composable
private fun DarkPreview() {
    CustomTheme(darkTheme = true) {
        content
    }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*one 6

如果您想要一个按钮/开关来更改主题并使其持久为设置,您还可以通过使用Jetpack DataStore(推荐)或SharedPreferences来实现此目的,在 MainActivity 中获取主题状态并将其传递给您的主题可组合项,以及任何地方你想修改它。

SharedPreferences您可以在此GitHub 存储库中找到完整的工作示例。

此示例使用 aSingletonHilt作为依赖项,并且对于您想要存储的所有首选项都有效。


mar*_*ino 6

根据文档,处理由用户操作触发的主题更改的官方方法(即通过自定义构建设置选择除系统主题之外的主题)是使用

AppCompatDelegate.setDefaultNightMode()
Run Code Online (Sandbox Code Playgroud)

仅此调用就可以处理大多数事情,包括重新启动任何活动(从而重新组合)。为此,我们需要:

  • setContent调用扩展的ActivityAppCompatActvity
  • 用户选择在应用程序每次启动时保留并应用(通过AppCompatDelegate
  • 要定义是否启用深色模式,您CustomTheme还应该考虑用户defaultNightMode偏好的值:
@Composable
fun CustomTheme(
  isDark: Boolean = isNightMode(),
  content: @Composable () -> Unit
) {
  MaterialTheme(
    colors = if (darkTheme) DarkColors else LightColors,
    content = content
  )
}

@Composable
private fun isNightMode() = when (AppCompatDelegate.getDefaultNightMode()) {
    AppCompatDelegate.MODE_NIGHT_NO -> false
    AppCompatDelegate.MODE_NIGHT_YES -> true
    else -> isSystemInDarkTheme()
}
Run Code Online (Sandbox Code Playgroud)

这很好,因为它避免了在 Activity 中获取该值只是将其传递给主题的需要CustomTheme(isDark = isDark)

本文将详细介绍上述所有内容并提供更多详细信息。