参考文章1
参考文章2
一、概念
创建项目之后,就会生成一个 项目名称+Theme 的 @Compose 函数,我们可以通过更改其中的颜色来完成对主题的修改。硬编码在配置的地方把值写死会无法支持主题切换,应该从主题中引用对应值,由于每个值都是实例还能通过 copy() 修改。
二、步骤
2.1 定义不同版本的颜色名称和值
方式一:在 Color.kt 中定义颜色的不同版本名称和值。
object AppColors { //正文 val textPrimary_Light = Color(0xCC000000) val textPrimary_Dark = Color(0xFF626567) //红色 val red = Color(0xFFFF0000) val red50 = Color(0x80FF0000) //透明 val transparent = Color(0x00FFFFFF) }方式二:在 res/values/colors.xml 中定义颜色的不同版本名称和值。
<resources> //正文 <color name="textPrimary_Light">#CC000000</color> <color name="textPrimary_Dark">#FF626567</color> //红色 <color name="red">#FFFF0000</color> <color name="red50">#80FF0000</color> //透明 <color name="transparent">#00FFFFFF</color> </resources>2.2 定义颜色集基类
- 在 Theme.kt 中创建颜色集类。
- 构造中塞入颜色名称供创建实例的时候指定具体颜色。
- 定义对应的属性并通过 MutableState 包裹提供状态,以便发生改变时所有引用该颜色的组合项会重组。
- 提供 copy() 支持调用处修改某个颜色。(可选)
class ThemeColors( //定义需要的颜色名称 textPrimary: Color, background: Color, ) { //颜色是有状态的,发生改变时所有引用这个颜色的组合项都会重组 var textPrimary by mutableStateOf(textPrimary) private set var background by mutableStateOf(background) private set //提供copy支持调用处修改某个颜色 // fun copy( // textPrimary: Color = this.textPrimary, // background: Color = this.background, // ): ThemeColors = ThemeColors( // textPrimary = textPrimary, // background = background, // ) }2.3 创建不同的颜色集实例(不同的主题色)
在 Theme.kt 中,创建不同的颜色集实例(即不同的主题),构造中指定具体的颜色值。暂不推荐从xml中读取,通过主题调用的颜色显示错误,未找到原因
//亮色主题(举例从 Color.kt 中读取) private val LightColors = ThemeColors( textPrimary = AppColors.TextPrimary_Light, background = AppColors.transparent, ) //暗色主题(举例从 colors.xml 中读取) private val DarkColors = ThemeColors( textPrimary = Color(R.color.textPrimary_Dark), background = Color(R.color.transparent), )2.4 定义全局调用入口
在 Theme.kt 中,声明一个由 CompositionLocal 包装的变量用来获取颜色集实例,声明一个单例用作全局调用入口,还包含了对类型的判断。
//声明一个CompositionLocal变量供调用处获取自定义的颜色集实例 //颜色集对象中的颜色值是确定的不会更改的,因此使用staticCompositionLocalOf 的提高性能 //设为private收纳进AppTheme中从统一入口获取,比起通过 LocalXXX 获取更符合直觉 private val LocalAppColors = staticCompositionLocalOf { LightColors } //声明一个单例,用作全局入口 object AppTheme { //用于获取自定义的颜色集 val colors: ThemeColors @Composable get() = LocalAppColors.current //提供枚举类用于传参判断类型 enum class ColorsType { LIGHT, DARK } }2.5 提供切换功能
@Composable fun AppTheme( colorsType: AppTheme.ColorsType = AppTheme.ColorsType.LIGHT, //指定颜色集 darkTheme: Boolean = isSystemInDarkTheme(), //自动识别是否开启暗黑模式 content: @Composable () -> Unit //需要包裹的内容 ) { //获取指定的颜色方案并添加动画(避免切换的时候一闪) val themeColors = if (darkTheme) addAnimationToAPPColors(DarkColors) else{ when (colorsType) { AppTheme.ColorsType.LIGHT -> addAnimationToThemeColors(LightColors) AppTheme.ColorsType.DARK -> addAnimationToThemeColors(DarkColors) } } //提供出去 CompositionLocalProvider(LocalAppColors provides themeColors){ //同时使用MaterialTheme的话就在这里包裹一下 content() } }2.6 添加切换动画(避免直接一闪)
/** * 为自定义颜色集添加过度动画(避免直接一闪) */ @Composable private fun addAnimationToThemeColors(themeColors: ThemeColors) = ThemeColors( textPrimary = animateColorAsState(themeColors = targetColor.textPrimary, TweenSpec(600), label = "textPrimary").value, background = animateColorAsState(themeColors = targetColor.background, TweenSpec(600), label = "background ").value, )2.7 组合项中调用颜色
Text(text = "", color = AppTheme.colors.textPrimary)