Jetpack Compose 动画(基于 value 的动画)

使用 animate*AsState 为单个值添加动画效果

[animate*AsState] 函数是 Compose 中最简单的动画 API,用于为单个值添加动画效果。您只需提供目标值(或结束值),该 API 就会从当前值开始向指定值播放动画。

下面举例说明了如何使用此 API 为 alpha 添加动画效果。只需将目标值封装在 [animateFloatAsState] 中,alpha 值就会是所提供值(在本例中为 1f0.5f)之间的动画值。

ts 复制代码
@Composable
private fun example1() {
    val enabled = remember { mutableStateOf(true) }
    val alpha = animateFloatAsState(if (enabled.value) 1f else 0.5f,
        label = "animateFloatAsState",
        animationSpec = tween(durationMillis = 800)
    )
  Column {
      customTitle(title = "使用 animate*AsState 为单个值添加动画效果")

      Button(modifier = Modifier.padding(start = 15.dp), onClick = { enabled.value = !enabled.value }) {
          Text(text = if (enabled.value) "半透明" else "非透明")
      }
      Box(
          Modifier.fillMaxWidth()
              .height(200.dp)
              .graphicsLayer(alpha = alpha.value)
              .background(Color.Red)
      )
  }
}

使用 Transition 同时为多个属性添加动画效果

将过渡效果与 AnimatedVisibilityAnimatedContent 搭配使用

ts 复制代码
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun example2() {
    val selected = remember { mutableStateOf(false) }
    // Animates changes when `selected` is changed.
    val transition = updateTransition(selected.value, label = "selected state")
    val borderColor = transition.animateColor(label = "border color") { isSelected ->
        if (isSelected) Color.Magenta else Color.White
    }
    val elevation = transition.animateDp(label = "elevation") { isSelected ->
        if (isSelected) 10.dp else 2.dp
    }
  Column {
      customTitle(title = "使用 animate*AsState 为单个值添加动画效果")
      Surface(
          shape = RoundedCornerShape(8.dp),
          border = BorderStroke(2.dp, borderColor.value),
          shadowElevation = elevation.value,
          modifier = Modifier.padding(all=5.dp).clickable {
              selected.value = !selected.value
          }
      ) {
          Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
              Text(text = "Hello, world!")
              // AnimatedVisibility 过渡
              transition.AnimatedVisibility(
                  visible = { targetSelected -> targetSelected },
                  enter = expandVertically(),
                  exit = shrinkVertically()
              ) {
                  Text(text = "It is fine today.")
              }
              // AnimatedContent 过渡
              transition.AnimatedContent { targetState ->
                  if (targetState) {
                      Text(text = "Selected")
                  } else {
                      Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone")
                  }
              }
          }
      }
      CodeView(assetUrl = "animation/animationValue/code1.txt", modifier = Modifier.padding(top = 10.dp))
  }
}

封装 Transition 并使其可重复使用

ts 复制代码
@Composable
private fun example3() {
    val boxState = remember {
        mutableStateOf(BoxState.Collapsed)
    }
    val transitionData = updateTransitionData(boxState.value)
    Column {
      customTitle(title = "使用 animate*AsState 为单个值添加动画效果")
      Box(
          modifier = Modifier
              .background(transitionData.color.value)
              .size(transitionData.size.value).clickable {
                  if (boxState.value == BoxState.Collapsed) {
                      boxState.value = BoxState.Expanded
                  } else {
                      boxState.value = BoxState.Collapsed
                  }
              }
      )
    }
}

enum class BoxState { Collapsed, Expanded }

// 保存动画的值 the animation values.
private data class TransitionData(
    val color: State<Color>,
    val size: State<Dp>
)

// Create a Transition and return its animation values.
@Composable
private fun updateTransitionData(boxState: BoxState): TransitionData {
    val transition = updateTransition(boxState, label = "box state")
    val color = transition.animateColor(label = "color", transitionSpec = {
        tween(durationMillis = 1000)
    }) { state ->
        when (state) {
            BoxState.Collapsed -> Color.Gray
            BoxState.Expanded -> Color.Red
        }
    }
    val size = transition.animateDp(label = "size", transitionSpec = {
        tween(durationMillis = 1000)
    }) { state ->
        when (state) {
            BoxState.Collapsed -> 64.dp
            BoxState.Expanded -> 128.dp
        }
    }
    return remember(transition) { TransitionData(color, size) }
}

使用 rememberInfiniteTransition 创建无限重复的动画

[InfiniteTransition] 可容纳一个或多个子动画(如 Transition),但这些动画一进入组合就会立即开始运行,并且不会停止,除非这些动画被移除。您可以使用 rememberInfiniteTransition 创建 InfiniteTransition 实例。您可以使用 animateColoranimatedFloatanimatedValue 添加子动画。您还需要指定 [infiniteRepeatable] 以指定动画规范。

ts 复制代码
@Composable
private fun example4() {
    val infiniteTransition = rememberInfiniteTransition(label = "")
    val color = infiniteTransition.animateColor(
        initialValue = Color.Red,
        targetValue = Color.Green,
        animationSpec = infiniteRepeatable(
            animation = tween(1000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        ), label = ""
    )
    Column {
        customTitle(title = "使用 rememberInfiniteTransition 创建无限重复的动画")
        Box(modifier = Modifier.size(200.dp).background(color.value))
    }
}

低级别动画 API

Animatable:基于协程的单值动画

[Animatable] 是一个值容器,可以在通过 animateTo 更改值时为值添加动画效果。这是支持 animate*AsState 实现的 API。它可确保一致的连续性和互斥性,这意味着值变化始终是连续的,并且会取消任何正在播放的动画。

Animatable 的许多功能(包括 animateTo)以挂起函数的形式提供。这意味着,它们需要封装在适当的协程作用域内。例如,您可以使用 LaunchedEffect 可组合项针对指定键值的时长创建一个作用域。

ts 复制代码
@Composable
private fun example5() {
    val ok = remember { mutableStateOf(false) }
    // Start out gray and animate to green/red based on `ok`
    val color = remember { Animatable(Color.Gray) }
    Column {
        customTitle(title = "Animatable:基于协程的单值动画")
        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable {
            ok.value = !ok.value
        }) {
            Checkbox(checked = ok.value, onCheckedChange = {
                ok.value = it
            })
            Text(text = "切换动画")
        }
        LaunchedEffect(ok.value) {
            color.animateTo(if (ok.value) Color.Green else Color.Red, animationSpec = tween(durationMillis = 1000))
        }
        Box(
            Modifier
                .size(200.dp)
                .background(color.value))
    }
}

Animation:手动控制的动画

[Animation] 是可用的最低级别的 Animation API。到目前为止,我们看到的许多动画都是基于 Animation 构建的。Animation 子类型有两种:[TargetBasedAnimation] 和 [DecayAnimation]。

Animation 只能用于手动控制动画的时间。Animation 是无状态的,它没有任何生命周期概念。它充当更高级别 API 使用的动画计算引擎。

ts 复制代码
@Composable
private fun example6() {
    val state = remember {
        mutableStateOf(0)
    }

    val anim = remember {
        TargetBasedAnimation(
            animationSpec = tween(2000),
            typeConverter = Float.VectorConverter,
            initialValue = 100f,
            targetValue = 300f
        )
    }
    val playTime = remember { mutableStateOf(0L) }

    val animationValue = remember {
        mutableStateOf(0)
    }
    Column {
        customTitle(title = "Animation:手动控制的动画")
        LaunchedEffect(state.value) {
            val startTime = withFrameNanos { it }
            do {
                playTime.value = withFrameNanos { it } - startTime
                animationValue.value = anim.getValueFromNanos(playTime.value).toInt()
            } while (!anim.isFinishedFromNanos(playTime.value))
        }
        Box(modifier = Modifier
            .size(animationValue.value.dp)
            .background(Color.Red,shape = RoundedCornerShape(animationValue.value / 5))
            .clickable {
                state.value++
            },contentAlignment = Alignment.Center) {
            Text(text = animationValue.value.toString(), style = TextStyle(color = Color.White,fontSize = (animationValue.value / 5).sp))
        }
    }
}

上一篇 Jetpack Compose 动画(动画修饰符和可组合项)

相关推荐
x02414 天前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton17 天前
深入理解观察者模式
android·kotlin·android jetpack
Wgllss17 天前
花式高阶:插件化之Dex文件的高阶用法,极少人知道的秘密
android·性能优化·android jetpack
上官阳阳20 天前
使用Compose创造有趣的动画:使用Compose共享元素
android·android jetpack
沐言人生24 天前
Android10 Framework—Init进程-15.属性变化控制Service
android·android studio·android jetpack
IAM四十二1 个月前
Android Jetpack Core
android·android studio·android jetpack
王能1 个月前
Kotlin真·全平台——Kotlin Compose Multiplatform Mobile(kotlin跨平台方案、KMP、KMM)
android·ios·kotlin·web·android jetpack·kmp·kmm
alexhilton1 个月前
让Activity更加优雅地跳转
android·kotlin·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-11.客户端操作属性
android·android studio·android jetpack
沐言人生1 个月前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack