Android Jetpack Compose(Composable) 流畅更换主题颜色

Android Jetpack Compose(Composable) Change Theme Color Smoothly

因为我不想使用基本颜色 class,所以我想使用我自己的颜色,这些颜色既不用于按钮也不用于文本元素。所以我用相应的浅色和深色调色板 class 创建了自己的颜色 class。然后在我的可组合项中我想更改主题颜色,但是使用动画,这就是为什么我使用 animateColorAsState,但代码看起来非常难看。有没有一种方法可以使所有颜色都具有动画效果,而无需分别为每种颜色定义 animateColorAsState?

这是结果

自定义调色板的代码

@Stable
class Colors(
    background: Color,
    line90DegColor: Color,
    hoursArrowColor: Color,
    secondsArrowColor: Color,
    baseColor: Color,
    lighter: Color,
    darker: Color,
    baseColorLessTransparent: Color,
    baseColorSemiTransparent: Color,
    baseColorTransparent: Color
) {
    var background by mutableStateOf(background, structuralEqualityPolicy())
        internal set

    var line90DegColor by mutableStateOf(line90DegColor, structuralEqualityPolicy())
        internal set

    // same for the rest of the colors

}

val LightColorPalette = Colors(
    background = Color(0xFFECECF3),
    line90DegColor = Color(0xFF9B9BB0),
    // same for the rest of the colors
)

val DarkColorPalette = Colors(
    background = Color(0xFF25252D),
    line90DegColor = Color(0xFF9B9BB0),
    // same for the rest of the colors
)

可组合项的代码,其中颜色通过 animateColorAsState

更改
class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        setContent {

            var isLightTheme by remember {
                mutableStateOf(true)
            }
            val colors = if (isLightTheme) {
                LightColorPalette
            } else {
                DarkColorPalette
            }

            // create color state for each of the theme color
            val animationSpec: AnimationSpec<Color> = tween(durationMillis = 1500)
            val background by animateColorAsState(colors.background, animationSpec)
            val line90DegColor by animateColorAsState(colors.line90DegColor, animationSpec)
            val hoursArrowColor by animateColorAsState(colors.hoursArrowColor, animationSpec)
            val secondsArrowColor by animateColorAsState(colors.secondsArrowColor, animationSpec)
            val baseColor by animateColorAsState(colors.baseColor, animationSpec)
            val lighter by animateColorAsState(colors.lighter, animationSpec)
            val darker by animateColorAsState(colors.darker, animationSpec)
            val baseColorLessTransparent by animateColorAsState(colors.baseColorLessTransparent, animationSpec)
            val baseColorSemiTransparent by animateColorAsState(colors.baseColorSemiTransparent, animationSpec)
            val baseColorTransparent by animateColorAsState(colors.baseColorTransparent, animationSpec)

            // create new Colors object that matches the current theme
            val animatedColors = Colors(
                background,
                line90DegColor,
                hoursArrowColor,
                secondsArrowColor,
                baseColor,
                lighter,
                darker,
                baseColorLessTransparent,
                baseColorSemiTransparent,
                baseColorTransparent
            )

            ClockWidget(animatedColors, hours, minutes, seconds) {
                isLightTheme = !isLightTheme
            }
        } 
    }
}

我认为您可以尝试使用 targetState = your theme 将您的代码包装到 AnimationContent 多么简单的方法。或者你可以这样写:

@Stable
data class Colors(
    val background: Color,
    val line90DegColor: Color,
    val hoursArrowColor: Color,
    val secondsArrowColor: Color,
    val baseColor: Color,
    val lighter: Color,
    val darker: Color,
    val baseColorLessTransparent: Color,
    val baseColorSemiTransparent: Color,
    val baseColorTransparent: Color
) {
    private val animationSpec: AnimationSpec<Color> = tween(durationMillis = 1500)

    @Composable
    private fun animateColor(
        targetValue: Color,
        finishedListener: ((Color) -> Unit)? = null
    ) = animateColorAsState(targetValue = targetValue, animationSpec = animationSpec).value
    
    @Composable
    fun switch() = copy(
        background = animateColor(background),
        secondsArrowColor = animateColor(secondsArrowColor),
        lighter = animateColor(lighter),
        darker = animateColor(darker),
        //etc
    )

并将您的变量 animatedColors 更改为

val animatedColors = (if (isLightTheme) LightColorPalette else DarkColorPalette).switch()

我建议您阅读 CompositionLocal