缘由
四点多刷的时候,看到这样一篇文章: 自定义View仿照立刻点赞数字切换作用,作者运用自定义制作的技术完成了数字切换的动态作用,也便是如图:
两图分别为立刻的作用和作者的完成
不得不说,作者仿照的很像,自定义制作玩的登峰造极,非常优异。不过,即使是这样简略的动效,运用 View 系统完成起来依然相对麻烦。对上文来说,作者运用的 Kotlin 代码也达到了约 170 行。
Composable
假如换成 Compose 呢?作为声明式框架,在处理这类动画上会不会有奇效?
答案是必定的!下面是最简略的完成:
Row(modifier = modifier) {
text.forEach {
AnimatedContent(
targetState = it,
transitionSpec = {
slideIntoContainer(AnimatedContentScope.SlideDirection.Up) with
fadeOut() + slideOutOfContainer(AnimatedContentScope.SlideDirection.Up)
}
) { char ->
Text(text = char.toString(), modifier = modifier.padding(textPadding), fontSize = textSize, color = textColor)
}
}
}
你没看错,这便是 Composable 对应的简略仿照,中心代码不过十行。它的大致作用如下:
能看到,在数字改动时,相应的动画作用现已非常相似。当然他还有小瑕疵,比方在 99 – 100 时,最终一位的 0 没有初始动画;比方在数字减少时,他的动画方向应该相反。但这两个问题都是可以加点代码解决的,这里中心仅仅思路
原理
与上文作者将每个数字作为一个全体对待不同,我将每一位独立处理。观察图片,动画的中心在于每一位有差异时要做动画处理,因而将每一位独自处理能更好的树立状况。
Jetpack Compose 是声明式 UI,状况的改动自然而然就导致 UI 的改动,咱们所需求做的仅仅在 UI 改动时加个动画就可以。而刚好,关于这种内容的改动,Compose 为咱们提供了开箱即用的微件:AnimatedContent
AnimatedContent
此 Composable 签名如下:
@Composable
fun <S> AnimatedContent(
targetState: S,
modifier: Modifier = Modifier,
transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = {
...
},
contentAlignment: Alignment = Alignment.TopStart,
content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit
)
要点在于 targetState
,在 content 内部,咱们需求获取到用到这个值,依据值的不同,呈现不同的 UI。AnimatedContent
会在 targetState
改动使自动对上一个 Composable 履行退出动画,并对新 Composable 履行进入动画 (有点幻灯片切换的感觉hh),在这里,咱们的动画是这样的:
slideIntoContainer(AnimatedContentScope.SlideDirection.Up)
with
fadeOut() + slideOutOfContainer(AnimatedContentScope.SlideDirection.Up)
上半部分的 slideIntoContainer
会履行进入动画,方向为自下向上;后半部分则是退出动画,由向上的路径动画和淡出结合而来。中缀函数 with
衔接它们。这也体现了 Kotlin 作为一门现代化语言的优雅。
关于 Compose 的更多常识,可以参阅 Compose 中文社区的大佬们一起维护的 Jetpack Compose 博物馆。
代码
本文的所有代码如下:
import androidx.compose.animation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun NumberChangeAnimationText(
modifier: Modifier = Modifier,
text: String,
textPadding: PaddingValues = PaddingValues(horizontal = 8.dp, vertical = 12.dp),
textSize: TextUnit = 24.sp,
textColor: Color = Color.Black
) {
Row(modifier = modifier) {
text.forEach {
AnimatedContent(
targetState = it,
transitionSpec = {
slideIntoContainer(AnimatedContentScope.SlideDirection.Up) with
fadeOut() + slideOutOfContainer(AnimatedContentScope.SlideDirection.Up)
}
) { char ->
Text(text = char.toString(), modifier = modifier.padding(textPadding), fontSize = textSize, color = textColor)
}
}
}
}
@Composable
fun NumberChangeAnimationTextTest() {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
var text by remember { mutableStateOf("103") }
NumberChangeAnimationText(text = text)
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
// 加一 和 减一
listOf(1, -1).forEach { i ->
TextButton(onClick = {
text = (text.toInt() + i).toString()
}) {
Text(text = if (i == 1) "加一" else "减一")
}
}
}
}
}
这个示例也被录入到了我的 JetpackComposeStudy: 自己 Jetpack Compose 主题文章所包含的示例,包括自定义布局、部分组件用法等 里,感兴趣的可以去那里检查更多代码。
最近敞开了2022的年度人气创作者评选,假如您对我的文章认可的话,欢迎投给我宝贵的一票,感谢!本文有协助的话,也欢迎点赞沟通。
(现在6点13分,连写代码加写文章共用了一个多小时,嗯,收工~)