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

Posted

技术标签:

【中文标题】Jetpack Compose:如何以编程方式将主题从浅色模式更改为深色模式 onClick【英文标题】:Jetpack Compose: How to change theme from light to dark mode programmatically onClick 【发布时间】:2021-03-19 09:21:34 【问题描述】:

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
   )
   

主题模型和状态变量

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"
    


组件(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)
                        
                    )
                )
            
        
   

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

【问题讨论】:

你在哪里应用你的主题? 我在 MainActivity 中应用我的主题,将它包裹在主 Composable 周围,就像样板 compose 项目设置一样。 【参考方案1】:

Jetpack theming codelab 中显示的一种可能性是通过输入参数设置暗模式,以确保在参数更改时重新组合主题:

@Composable
fun CustomTheme(
  darkTheme: Boolean = isSystemInDarkTheme(),
  content: @Composable () -> Unit
) 
  MaterialTheme(
    colors = if (darkTheme) DarkColors else LightColors,
    content = content
  )

在您的 mainActivity 中,您可以观察 viewModel 的变化并将其传递给您的 customTheme:

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

CustomTheme(darkTheme.value)
//yourContent

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

@Composable
private fun DarkPreview() 
    CustomTheme(darkTheme = true) 
        content
    

【讨论】:

我认为更改被正确观察,我可以说,因为当我将系统设置从浅色主题切换到深色主题时,应用程序会响应。我想不通的是如何通过单击按钮将主题从暗切换到亮。 您在可组合设置中观察到主题更改,但在自定义主题中没有:val colors = when (themeViewModel.theme.value.toString()),因此它们不会应用于您的主题 即使我观察到自定义主题的变化,当主题发生变化时,应用程序也不会重新组合。您的建议无效,但谢谢。 你确定你使用的是同一个viewModel实例吗?【参考方案2】:

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

您可以在 GitHub repo 中找到带有 SharedPreferences 的完整工作示例。

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

【讨论】:

【参考方案3】:

这可能不是推荐的方式,但一种选择是使用recreate method(从 API 级别 11 开始可用)。

为了在 Activity 之外和可组合项中使用它,您可以传递函数调用。以你的代码为基础

class SomeActivity 
    override fun onCreate(savedInstanceState: Bundle?) 
        ...
        setContent 
            ...
            themeViewModel.onRecreate =  recreate() 

            CustomTheme(themeViewModel) 
                ...
            
        
    

在您的视图模型中

class ThemeViewModel: ViewModel() 

    lateinit var onRecreate: () -> Unit

    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"
        
        onRecreate
    

【讨论】:

以上是关于Jetpack Compose:如何以编程方式将主题从浅色模式更改为深色模式 onClick的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Jetpack Compose 的 LazyColumn/LazyRow 中禁用和启用滚动?

Android全新UI编程 - Jetpack Compose 超详细教程

在 Jetpack Compose 中安全地使用数据流

在 Jetpack Compose 中安全地使用数据流

《Jetpack Compose系列学习》-2 Compose编程思想

Jetpack Compose+架构=优秀APP?