Jetpack Compose实现动画效果的方法详解

目录
  • 概述
  • 低级别动画API
    • animate*AsState
    • 使用Animatable实现颜色变化效果
    • 使用updateTransition实现颜色和圆角动画
    • rememberInfiniteTransition
    • TargetBasedAnimation
  • 自定义动画
    • AnimationSpec
    • Easing
    • AnimationVector
  • 高级动画

概述

compose 为支持动画提供了大量的 api,通过这些 api 我们可以轻松实现动画效果

ps:这些 api 的原理与 Flutter 很接近,与原生的 api 相去甚远

你可以提前看看用 compose 实现的一个放大缩小动画,总的来说还是比较流畅:

低级别动画 API

animate*AsState

所能处理属性的种类:Float、Color、Dp、Size、Bounds、Offset、Rect、Int、IntOffset 和 IntSize

通过 animate*AsState 我们可以实现单一属性的动画效果,我们只需要提供目标值就可以自动从当前进度动画过渡到目标值

实现放大动画

1.代码

@Composable
fun animSize() {
    val enable = remember {
        mutableStateOf(true)
    }
    val size =
        animateSizeAsState(targetValue = if (enable.value) Size(50f, 50f) else Size(300f, 300f))
    Column(
        modifier = Modifier.fillMaxSize(1f),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Image(
            modifier = Modifier
                .size(size.value.width.dp, size.value.height.dp)
                .clickable {
                    enable.value = !enable.value
                },
            painter = painterResource(id = R.drawable.apple),
            contentDescription = ""
        )
    }
}

2.实现效果

实现颜色变化动画

1.代码

@Composable
fun animColor() {
    val enable = remember {
        mutableStateOf(true)
    }
    val colors = animateColorAsState(targetValue = if (enable.value) Color.Green else Color.Red)
    val size = animateIntSizeAsState(
        targetValue = if (enable.value) IntSize(100, 100) else IntSize(
            300,
            300
        )
    )
    Column(
        modifier = Modifier.fillMaxWidth(1f),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Box(
            modifier = Modifier
                .size(size.value.width.dp, size.value.height.dp)
                .height(400.dp)
                .background(
                    color = colors.value,
                    shape = if (enable.value) RectangleShape else CircleShape
                )
        ) {

        }
    }
}

2.效果

使用 Animatable 实现颜色变化效果

Animatable 是一个值容器,我们可以通过调用 animateTo 实现动画效果。动画执行过程中如果再次开启动画会中断当前动画。

Animatable 动画执行过程中值的变化是在协程中执行的,所以 animateTo 是一个挂起操作

1.代码

@Composable
fun animChangeColor() {
    val color = remember {
        Animatable(Color.Red)
    }
    val state = remember {
        mutableStateOf(true)
    }
    LaunchedEffect(state.value) {
        color.animateTo(if (state.value) Color.Red else Color.Magenta)
    }
    Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .background(color.value, shape = RoundedCornerShape(30.dp))
                .size(200.dp)
                .clickable {
                    state.value = !state.value
                }, contentAlignment = Alignment.Center
        ) {
            Text(
                text = "颜色动画",
                style = TextStyle(color = Color.White, fontSize = 40.sp)
            )
        }
    }
}

2.效果

使用 updateTransition 实现颜色和圆角动画

使用 updateTransition 可以实现多个动画组合的效果。

例如:我们可以在动画执行过程中同时执行大小和颜色变化效果

本例中我们定义了一个枚举用来控制动画,枚举可以定义多个,分别用来对应动画的多个状态

1.代码

@Composable
fun animupdateTransition() {
    var state by remember {
        mutableStateOf(BoxState.Collapsed)
    }
    val transition = updateTransition(targetState = state, label = "")

    val round = transition.animateDp(label = "") {
        when (it) {
            BoxState.Collapsed -> 40.dp
            BoxState.Expanded -> 100.dp
        }
    }
    val color = transition.animateColor(label = "") {
        when (it) {
            BoxState.Collapsed -> Color.Red
            BoxState.Expanded -> Color.Green
        }
    }
    Box(Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .size(300.dp)
                .background(
                    color.value,
                    shape = RoundedCornerShape(corner = CornerSize(round.value))
                )
                .clickable {
                    state =
                        if (state == BoxState.Collapsed) BoxState.Expanded else BoxState.Collapsed
                },contentAlignment = Alignment.Center
        ) {
            Text(text = "点击开始动画",style = TextStyle(color = Color.White,fontSize = 20.sp))
        }
    }
}
private enum class BoxState {
    Collapsed,
    Expanded
}

2.效果

rememberInfiniteTransition

rememberInfiniteTransition 的使用和 updateTransition 基本一样,不同的是 rememberInfiniteTransition 的动画一旦开始便会一直反复运行下去,只有被移除动画才能结束

1.代码

@Composable
fun rememberInfiniteTransition1() {
    val infiniteTransition = rememberInfiniteTransition()
    val color by infiniteTransition.animateColor(
        initialValue = Color.Red,
        targetValue = Color.Green,
        animationSpec = infiniteRepeatable(
            animation = tween(1000, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )

    Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.Center) {
        Box(
            Modifier
                .fillMaxSize(0.8f)
                .background(color),
            contentAlignment = Alignment.Center
        ) {
            Text(
                text = "公众号:安安安安卓 原创,禁抄袭",
                style = TextStyle(color = Color.White, fontSize = 30.sp)
            )
        }
    }
}

2.效果 ​​​​​​​

TargetBasedAnimation

TargetBasedAnimation 可以控制动画的执行时间,还可以延迟一段时间再开启动画。

1.代码

@Composable
fun animTargetBasedAnimation() {
    var state by remember {
        mutableStateOf(0)
    }
    val anim = remember {
        TargetBasedAnimation(
            animationSpec = tween(2000),
            typeConverter = Float.VectorConverter,
            initialValue = 100f,
            targetValue = 300f
        )
    }
    var playTime by remember { mutableStateOf(0L) }
    var animationValue by remember {
        mutableStateOf(0)
    }

    LaunchedEffect(state) {
        val startTime = withFrameNanos { it }
        println("进入协程:")
        do {
            playTime = withFrameNanos { it } - startTime
            animationValue = anim.getValueFromNanos(playTime).toInt()
        } while (!anim.isFinishedFromNanos(playTime))

    }
    Box(modifier = Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) {
        Box(modifier = Modifier
            .size(animationValue.dp)
            .background(Color.Red,shape = RoundedCornerShape(animationValue/5))
            .clickable {
                state++
            },contentAlignment = Alignment.Center) {
            Text(text = animationValue.toString(),style = TextStyle(color = Color.White,fontSize = (animationValue/5).sp))
        }
    }
}

2.效果 ​​​​​​​

自定义动画

AnimationSpec

AnimationSpec 可以自定义动画的行为,效果类似于原生动画中的估值器。

SpringSpec 弹簧效果

1.代码

@Composable
fun animSpring() {
    val state = remember {
        mutableStateOf(true)
    }
    var value = animateIntAsState(
        targetValue = if (state.value) 300 else 100,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioHighBouncy,
            stiffness = Spring.StiffnessVeryLow
        )
    )

    Box(
        Modifier
            .fillMaxSize(1f)
            .padding(start = 30.dp), contentAlignment = Alignment.CenterStart
    ) {
        Box(
            Modifier
                .width(value.value.dp)
                .height(80.dp)
                .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                .clickable {
                    state.value = !state.value
                }, contentAlignment = Alignment.CenterStart
        ) {
            Text(text = "哈哈哈", style = TextStyle(color = Color.White, fontSize = 20.sp))
        }
    }
}

2.效果 ​​​​​​​

TweenSpec 动画时间可控

1.代码

@Composable
fun animTweenSpec() {
    val state = remember {
        mutableStateOf(true)
    }
    val value = animateIntAsState(
        targetValue = if (state.value) 300 else 100,
        animationSpec = tween(
            durationMillis = 1500,
            delayMillis = 200,
            easing = LinearEasing
        )
    )

    Box(
        Modifier
            .fillMaxSize(1f)
            .padding(start = 50.dp), contentAlignment = Alignment.CenterStart
    ) {
        Box(
            Modifier
                .width(value.value.dp)
                .height(100.dp)
                .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                .clickable {
                    state.value = !state.value
                }
        ) {

        }
    }

}

2.效果 ​​​​​​​

FrameSpec

1.代码

@Composable
fun animkeyframesSpec() {
    var state by remember {
        mutableStateOf(true)
    }
    val value by animateIntAsState(
        targetValue = if (state) 300 else 100,
        animationSpec = keyframes {
            durationMillis = 2000
            0 at 700 with LinearOutSlowInEasing
            700 at 1400 with FastOutLinearInEasing
            1400 at 2000
        })

    Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.CenterStart) {
        Box(
            Modifier
                .width(value.dp)
                .height(100.dp)
                .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                .clickable {
                    state = !state
                }
        ) {

        }
    }
}

2.效果

RepeatableSpec 实现有限次数的重复动画

执行有限次数动画后自动停止

1.代码

@Composable
fun animrepeatableSpec() {
    var state by remember {
        mutableStateOf(true)
    }
    val value by animateIntAsState(
        targetValue = if (state) 300 else 100,
        animationSpec = repeatable(
            iterations = 5,//动画重复执行的次数,设置多少就执行多少次
            animation = tween(durationMillis = 1000),
            repeatMode = RepeatMode.Reverse
        )
    )
    Box(
        Modifier
            .fillMaxSize(1f)
            .padding(start = 30.dp), contentAlignment = Alignment.CenterStart) {
        Box(
            Modifier
                .width(value.dp)
                .height(100.dp)
                .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                .clickable {
                    state = !state
                }
        ) {

        }
    }
}

2.效果

代码中设置了重复 5 次,所以反复执行五次后动画结束

InfiniteRepeatableSpec 无限次数执行动画

动画会无限次的执行下去,直到视图被移除

1.代码

@Composable
fun animinfiniteRepeatableSpec() {
    var state by remember {
        mutableStateOf(true)
    }
    val value by animateIntAsState(
        targetValue = if (state) 300 else 100,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1000),
            repeatMode = RepeatMode.Reverse
        )
    )
    Box(
        Modifier
            .fillMaxSize(1f)
            .padding(start = 30.dp), contentAlignment = Alignment.CenterStart) {
        Box(
            Modifier
                .width(value.dp)
                .height(100.dp)
                .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                .clickable {
                    state = !state
                }
        ) {
            Text(text = "公众号:安安安安卓 原创,禁转载")
        }
    }
}

2.效果

Easing

Easing 类似于我们原生动画中的差值器

有以下几种选择:

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearInEasing
  • LinearEasing
  • CubicBezierEasing

这几种实现的效果和 android 原生实现的动画差值器差距很大,甚至看不出有啥效果,所以代码我就不放了。有清楚原因的读者可以联系我

实现效果:

AnimationVector

大多数 Compose 动画 API 都支持将 Float、Color、Dp 以及其他基本数据类型作为开箱即用的动画值,但有时我们需要为其他数据类型(包括我们的自定义类型)添加动画效果

本例中实现颜色和大小的变换动画

代码中我们定义了一个 AnimSize 类,类中的第一个参数是颜色数据,第二个参数是尺寸数据。动画执行过程中会同事改变颜色和控件尺寸效果。

1.代码

@Composable
fun animAnimationVector() {
    var state by remember {
        mutableStateOf(true)
    }
    val value by animateValueAsState(
        targetValue = if (state) AnimSize(0xffff5500, 100f) else AnimSize(0xff00ff00, 300f),
        typeConverter = TwoWayConverter(
            convertToVector = {
//                AnimationVector2D(target.color.toFloat(), target.size)
                AnimationVector2D(it.color.toFloat(), it.size)
            },
            convertFromVector = {
                AnimSize(it.v1.toLong(), it.v2)
            }
        )
    )
    println("颜色:${value.color}")
    Box(modifier = Modifier.fillMaxSize(1f).padding(30.dp), contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .size(value.size.dp)
//                .size(300.dp)
                .background(Color(value.color), RoundedCornerShape(30.dp))
                .clickable {
                    state = !state
                }
        ) {

        }
    }
}

data class AnimSize(val color: Long, val size: Float)

2.效果

缺点是执行颜色变化过程中有闪烁

高级动画

高级动画一般指封装性较高的动画,使用较为简单,主要有以下三种:

因高级动画效果不明显,gif 很难展现出效果,所以这里不放代码和效果图了

  1. AnimatedVisibility
  2. animateContentSize
  3. Crossfade

以上就是Jetpack Compose实现动画效果的方法详解的详细内容,更多关于Jetpack Compose动画的资料请关注我们其它相关文章!

(0)

相关推荐

  • 利用Jetpack Compose实现主题切换功能

    目录 前言 color.kt Theme.kt 关于compositionLocalOf 完整代码 前言 新建的Compose项目默认的 Material 主题为我们提供了一些颜色,但对我这种花里胡哨的人来说根本不够呀. 所以系统提供的主题不能满足需求时候可以自己配置主题 compose 实现换肤很简单 之前xml方法可复杂了 通过LayoutInflater调用inflate方法加载XML布局,在inflate方法中有一个createViewFromTag,再根据LayoutInflater当

  • Android Jetpack Compose实现列表吸顶效果

    目录 stickyHeader 实体类 加载假数据 吸顶标题 二级条目 完整代码 效果图 安卓传统的 Recyclerview 打造悬浮头部StickyHeader的吸顶效果,十分麻烦,而在Compose中就简单多了 stickyHeader Compose设计的时候考虑得很周到,他们提供了stickyHeader 作用就是添加一个粘性标题项,即使在它后面滚动时也会保持固定.标头将保持固定,直到下一个标头取而代之. 参数key - 表示唯一的密钥键. 它不允许对列表出现使用相同的键.密钥的类型应

  • 通过Jetpack Compose实现双击点赞动画效果

    目录 实现步骤 先红色画个爱心 点击事件加动画 完整代码 效果图 实现步骤 先红色画个爱心 Icon( Icons.Filled.Favorite, "爱心", Modifier .align(Alignment.Center) tint = Color.Red ) 点击事件加动画 双击监听 .pointerInput(Unit) { detectTapGestures( onDoubleTap = { ... } ) } #### **API 介绍** | API名称 | 作用 |

  • 利用Jetpack Compose绘制可爱的天气动画

    目录 1. 项目背景 2. MyApp:CuteWeather App界面构成 3. Compose自定义绘制 声明式地创建和使用Canvas 强大的DrawScope 4.简单易用的API 使用原生Canvas 5. 雨天效果 雨滴的绘制 雨滴下落动画 6.Compose自定义布局 7.. 雪天效果 雪花的绘制 雪花飘落动画 雪花的自定义布局 8. 晴天效果 太阳的绘制 太阳的旋转 9. 动画的组合.切换 将图形组合成天气 ComposedIcon ComposedWeather 1. 项目背

  • Android Jetpack Compose无限加载列表

    目录 前言 方法一: paging-compose 方法二:自定义实现 添加 LoadingIndicator 总结 前言 Android 中使用 ListView 或者 RecycleView 经常有滚动到底部自动 LoadMore 的需求,那么在 Compose 中该如何实现呢? 两种方法可供选择: 基于 paging-compose 自定义实现 方法一: paging-compose Jetpack 的 Paging 组件提供了对 Compose 的支持 dependencies { ..

  • Jetpack Compose实现动画效果的方法详解

    目录 概述 低级别动画API animate*AsState 使用Animatable实现颜色变化效果 使用updateTransition实现颜色和圆角动画 rememberInfiniteTransition TargetBasedAnimation 自定义动画 AnimationSpec Easing AnimationVector 高级动画 概述 compose 为支持动画提供了大量的 api,通过这些 api 我们可以轻松实现动画效果 ps:这些 api 的原理与 Flutter 很接

  • Android Flutter实现GIF动画效果的方法详解

    目录 前言 交错动画机制 代码实现 Interval 介绍 总结 前言 我们之前介绍了不少有关动画的篇章.前面介绍的动画都是只有一个动画效果,那如果我们想对某个组件实现一组动效,比如下面的效果,该怎么办? staggered animation 这个时候我们需要用到组合动效, Flutter 提供了交错动画(Staggered Animation)的方式实现.对于多个 Anmation 对象,可以共用一个 AnimationController,然后在不同的时间段执行动画效果.这就有点像 GIF

  • jQuery实现基本动画效果的方法详解

    本文实例讲述了jQuery实现基本动画效果的方法.分享给大家供大家参考,具体如下: animate()方法用于创建自定义动画 语法: $(selector).animate({params},speed,callback); params:必须 定义形成动画的CSS属性 speed:可选  规定效果的时常:slow,fast或毫秒 callback:可选  动画完成后所执行的函数名称. jQuery animate()--操作多个属性 默认情况下,所有HTML元素的位置都是静态的,并且无法移动,

  • vue实现购物车抛物线小球动画效果的方法详解

    本文实例讲述了vue实现购物车抛物线小球动画效果的方法.分享给大家供大家参考,具体如下: 先上最终效果图,在商品页面和商品详情页面点击加号添加商品时都可以看到小球抛物线落入购物车的动画效果 此文章只写了商品页面购物小球的实现,商品详情页原理类似 实现步骤: 1. 需要三个组件,最下方包含蓝色购物车的[购物车]组件shopCart.vue(子组件),每个[加减号]组成的购物小球组件cartControl.vue(子组件),和包含每个商品信息的goods组件goods.vue(父组件) 2. 原理,

  • Android TextView渐变颜色和方向及动画效果的设置详解

    GradientTextView Github点我 一个非常好用的库,使用kotlin实现,用于设置TexView的字体 渐变颜色.渐变方向 和 动画效果 添加依赖 之前仓库发布在 jcenter,但是因为它即将不可用,近期已完成迁移.建议大家使用 mavenCentral 的配置. 使用 jcenter implementation 'com.williamyang:gradienttext:1.0.1' 使用 mavenCentral buildscript { repositories {

  • 微信小程序实现下拉刷新和上拉分页效果的方法详解

    目录 下拉刷新 上拉分页 下拉刷新 下拉刷新这个玩意吧,很有用,但是在我博客关联的小程序中,用处不大,也是,我那个小程序一共也没有几个页…… 我这里还是用在首页,上拉分页,下拉刷新重载分页.我就是这么做的. 下拉刷新和上拉分页还是有区别的. 下拉刷新需要在index.json中添加属性: "enablePullDownRefresh": true Index.js Page({ data: { // 文章数组 articleList:[], //每页显示的行数: pagesize: 2

  • JS实现滑动门效果的方法详解

    本文实例讲述了JS实现滑动门效果的方法.分享给大家供大家参考,具体如下: 描述:鼠标移动到一副图片上,会显示该副图片的全貌,而其他图片会显示概貌,效果图如下: 一.没有动画效果的运动 思路: 1.定好每张图片的初始位置(第一张完全显示,234只露出一部分) 2.计算每道门的移动距离(即未显露的部分) 3.绑定鼠标滑过事件 window.onload=function(){ var box=document.getElementById("box"); var img=box.getEl

  • JS实现焦点图轮播效果的方法详解

    本文实例讲述了JS实现焦点图轮播效果的方法.分享给大家供大家参考,具体如下: 效果图如下: 一.所用到的知识点 1.DOM操作 2.定时器 3.事件运用 4.Js动画 5.函数递归 6.无限滚动大法 二.结构和样式 <div id="banner" class="banner"> <ul id="list-banner" class="list-banner fn-clear" style="lef

  • Android编程实现仿优酷圆盘旋转菜单效果的方法详解【附demo源码下载】

    本文实例讲述了Android编程实现仿优酷圆盘旋转菜单效果的方法.分享给大家供大家参考,具体如下: 目前,用户对安卓应用程序的UI设计要求越来越高,因此,掌握一些新颖的设计很有必要. 比如菜单,传统的菜单已经不能满足用户的需求. 其中优酷中圆盘旋转菜单的实现就比较优秀,这里我提供下我的思路及实现,仅供参考. 该菜单共分里外三层导航菜单.可以依次从外向里关闭三层菜单,也可以反向打开,并且伴有圆盘旋转的动画效果 首先,看下效果: 以下是具体的代码及解释: 1. 菜单布局文件: 大家看到主要有三个Ra

  • vue实现标签云效果的方法详解

    本文实例讲述了vue实现标签云效果的方法.分享给大家供大家参考,具体如下: 闲扯两句 最近想给自己的博客上加上一个3D标签云的效果,用来表示自己博客文章的分组,网上找到了canvas实现的,还有a元素实现的解析3D标签云,我想让标签可以选择和点击,又不想在标签数量较多时操作a标签导致性能问题,于是svg就成了一个不错的选择. 标签初始化 这里实现的核心主要是参考了前面的那篇解析3D标签云的文章,作者给出了源码,讲解也比较通俗易懂.大体来说,整个代码分三步: 根据标签的数量,算出每个标签在球面上分

随机推荐