Jetpack Compose动画详解-带效果图

简介: Jetpack Compose动画详解-带效果图

关于compose动画的文章


fun捞-# 玩转 Compose 动画:手把手教你实现微博点赞彩虹特效


概述


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

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

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

image.png


低级别动画 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 = ""
        )
    }
}
复制代码
  1. 实现效果

image.png


实现颜色变化动画

  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
                )
        ) {
        }
    }
}
复制代码
  1. 效果

image.png


使用 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)
            )
        }
    }
}
复制代码
  1. 效果

image.png


使用 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
}
复制代码
  1. 效果

image.png


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)
            )
        }
    }
}
复制代码
  1. 效果

image.png


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))
        }
    }
}
复制代码
  1. 效果

image.png


自定义动画


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))
        }
    }
}
复制代码
  1. 效果

image.png


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
                }
        ) {
        }
    }
}
复制代码
  1. 效果

image.png


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
                }
        ) {
        }
    }
}
复制代码
  1. 效果

image.png


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
                }
        ) {
        }
    }
}
复制代码
  1. 效果

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

image.png


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 = "公众号:安安安安卓 原创,禁转载")
        }
    }
}
复制代码
  1. 效果

image.png


Easing

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

有以下几种选择:

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearInEasing
  • LinearEasing
  • CubicBezierEasing

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

实现效果:

image.png


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)
复制代码
  1. 效果

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

image.png


高级动画


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

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

  1. AnimatedVisibility
  2. animateContentSize
  3. Crossfade



相关文章
|
3月前
|
安全 Java Android开发
探索安卓应用开发的新趋势:Kotlin和Jetpack Compose
在安卓应用开发领域,随着技术的不断进步,新的编程语言和框架层出不穷。Kotlin作为一种现代的编程语言,因其简洁性和高效性正逐渐取代Java成为安卓开发的首选语言。同时,Jetpack Compose作为一个新的UI工具包,提供了一种声明式的UI设计方法,使得界面编写更加直观和灵活。本文将深入探讨Kotlin和Jetpack Compose的特点、优势以及如何结合使用它们来构建现代化的安卓应用。
69 4
|
5月前
|
存储 移动开发 Android开发
使用kotlin Jetpack Compose框架开发安卓app, webview中h5如何访问手机存储上传文件
在Kotlin和Jetpack Compose中,集成WebView以支持HTML5页面访问手机存储及上传音频文件涉及关键步骤:1) 添加`READ_EXTERNAL_STORAGE`和`WRITE_EXTERNAL_STORAGE`权限,考虑Android 11的分区存储;2) 配置WebView允许JavaScript和文件访问,启用`javaScriptEnabled`、`allowFileAccess`等设置;3) HTML5页面使用`<input type="file">`让用户选择文件,利用File API;
|
6月前
|
JavaScript Java Android开发
kotlin安卓在Jetpack Compose 框架下跨组件通讯EventBus
**EventBus** 是一个Android事件总线库,简化组件间通信。要使用它,首先在Gradle中添加依赖`implementation &#39;org.greenrobot:eventbus:3.3.1&#39;`。然后,可选地定义事件类如`MessageEvent`。在活动或Fragment的`onCreate`中注册订阅者,在`onDestroy`中反注册。通过`@Subscribe`注解方法处理事件,如`onMessageEvent`。发送事件使用`EventBus.getDefault().post()`。
|
6月前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
6月前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
6月前
|
监控 Android开发 数据安全/隐私保护
安卓kotlin JetPack Compose 实现摄像头监控画面变化并录制视频
在这个示例中,开发者正在使用Kotlin和Jetpack Compose构建一个Android应用程序,该程序 能够通过手机后置主摄像头录制视频、检测画面差异、实时预览并将视频上传至FTP服务器的Android应用
|
6月前
深入了解 Jetpack Compose 中的 Modifier
深入了解 Jetpack Compose 中的 Modifier
106 0
|
6月前
|
Android开发
Jetpack Compose: Hello Android
Jetpack Compose: Hello Android
|
6月前
|
安全 网络安全 API
kotlin安卓开发JetPack Compose 如何使用webview 打开网页时给webview注入cookie
在Jetpack Compose中使用WebView需借助AndroidView。要注入Cookie,首先在`build.gradle`添加WebView依赖,如`androidx.webkit:webkit:1.4.0`。接着创建自定义`ComposableWebView`,通过`CookieManager`设置接受第三方Cookie并注入Cookie字符串。最后在Compose界面使用这个自定义组件加载URL。注意Android 9及以上版本可能需要在网络安全配置中允许第三方Cookie。
|
6月前
|
Android开发 Kotlin
kotlin安卓开发【Jetpack Compose】:封装SnackBarUtil工具类方便使用
GPT-4o 是一个非常智能的模型,比当前的通义千问最新版本在能力上有显著提升。作者让GPT开发一段代码,功能为在 Kotlin 中使用 Jetpack Compose 框架封装一个 Snackbar 工具类,方便调用