一个小时入门 Android Compose 动画

0. 前言

前段时间对于Android中的Compose动画做了系统性的学习,相关文章发布在 Compose 动画
专栏里。系统性学完Compose动画后,又对此做了系统性的回顾,抽取其比较重要的部分,希望能帮助大家快速入门Compose动画,所以有了本篇文章 : 一个小时入门 Android Compose 动画

1. Compose中的动画API

我们先来看下官网上的图,看上去很复杂,东西很多
在这里插入图片描述

对此,我进行了重新的整理。其实需要重点关注的Compose动画API,有这些
在这里插入图片描述
接下来我们一个个的来看

2. AnimateXxxAsState

AnimateXxxAsState用来实现单个值变化(大小、位置、颜色等)的动画
Xxx代指很多单位,比如DpColorFloatIntRect等。

我们这里以DpColor为例

  • animateDpAsState : 大小变化
  • animateColorAsState : 颜色变化

2.1 代码示例

下面这段代码点击后,Box的尺寸和颜色会发生动画过渡的变化

var big by remember {
    mutableStateOf(false)
}
//var size = if (big) 300.dp else 50.dp
val size by animateDpAsState(targetValue = if (big) 300.dp else 50.dp)
val color by animateColorAsState(targetValue = if (big) Color.Red else Color.Blue)
Box(
    Modifier
        .size(size)
        .background(color)
        .clickable {
            big = !big
        }
)

效果如下所示

在这里插入图片描述

3. AnimatedVisibility

控制UI组件的显示/隐藏,并可以自定义入场和出场时候的动画效果

3.1 入场和出场动画

AnimatedVisibility通过enter/exit可以配置入场和出场动画效果,比如以下几个

  • 淡入 : fadeIn / fadeout
  • 缩放 : scaleIn / scaleOut
  • 滑动 : slideIn / slideOut
  • 展开 : expandIn / shrinkOut

3.1 代码示例

这段代码点击按钮后,会 显示/隐藏 图片

Column(horizontalAlignment = Alignment.CenterHorizontally) {
    var visible by remember {
        mutableStateOf(true)
    }
    AnimatedVisibility(visible = visible, enter = expandIn(), exit = shrinkOut()) {
        Image(painter = painterResource(id = R.drawable.photot1), contentDescription = null)
    }
    Button(onClick = {
        visible = !visible
    }) {
        Text(text = "显示/隐藏")
    }
}

效果如下所示
在这里插入图片描述

4. Transition

用来管理多个动画,通过updateTransition()来创建一个Transition,再通过Transition创建具体的动画

4.1 代码示例

这段使用Transition实现的代码,效果和使用animateXxxAsState实现的效果一样

var big by remember {
    mutableStateOf(false)
}
val transition = updateTransition(targetState = big, label = "myTransition")
val size by transition.animateDp(label = "mySize") {
    if (it) 300.dp else 50.dp
}
val color by transition.animateColor(label = "myColor") {
    if (it) Color.Red else Color.Blue
}
//val size by animateDpAsState(targetValue = if (big) 300.dp else 50.dp)
//val color by animateColorAsState(targetValue = if (big) Color.Red else Color.Blue)
Box(
    Modifier
    .size(size)
    .background(color)
    .clickable {
        big = !big
    }
)

效果如下所示

在这里插入图片描述

4.2 为什么要有Transition?

animateXxAsState可以实现一样的效果,那为什么还要有 Transition 这个API呢 ?

  • 使用Transition可以对动画做很方便的管理
    • animateXxAsState是面向值的,在多个动画多个状态的情况下存在不便于管理的问题
    • Transition是面向状态的,多个动画可以共用一个状态,能够做到统一的管理
  • Transition支持Compose动画预览
    • Transition支持Compose动画预览功能
    • animateXxAsState当前不支持Compose动画预览

4.3 如何进入Compose动画预览界面 ?

点击Start Animation Preview按钮即可进入

在这里插入图片描述

4.4 封装并复用Transition

在简单的场景下,在同一个页面中使用updateTransition创建Transition并直接操作它完成动画是没问题的。然而,如果需要处理一个具有许多动画属性的复杂场景,可以把Transition动画的实现与用户界面分开,从而提升代码复用率和可维护性。

class TransitionBean(size: State<Dp>, color: State<Color>) {
    val size by size
    val color by color
}

@Composable
private fun updateMyTransition(big: Boolean): TransitionBean {
    val transition = updateTransition(targetState = big, label = "myTransition")
    val size = transition.animateDp(label = "mySize") {
        if (it) 300.dp else 50.dp
    }
    val color = transition.animateColor(label = "myColor") {
        if (it) Color.Red else Color.Blue
    }
    return TransitionBean(size, color)
}

@Composable
private fun TransitionTest() {
    var big by remember {
        mutableStateOf(false)
    }
    val transitionBean = updateMyTransition(big)
    Box(
        Modifier
            .size(transitionBean.size)
            .background(transitionBean.color)
            .clickable {
                big = !big
            }
    )
}

5. AnimationSpec

自定义动画规格 AnimationSpec,类似于传统View体系中的差值器Interpolator,但是比起差值器,又提供了更多的功能。
在这里插入图片描述

5.1 SpringSpec

基于弹簧的物理动画效果,通过spring()进行调用。

  • 很多动画内部AnimationSpec使用的默认值都是spring,比如animateXXXAsState以及updateTransition等
  • 基于物理规律,使动画更真实自然
  • 因为是基于物理规律的,所以无法指定动画执行时长,而是会基于物理规律来确定动画执行时长

5.1.1 SpringSpec的参数

  • dampingRatio :弹簧的阻尼比,这个值越大,阻尼越大
  • stiffness :弹簧的刚度值,弹簧有多想回弹回去,这个值越大,回弹的越快
  • visibilityThreshold :当动画到达这个阈值会立即停止

5.1.2 代码示例

var big by remember {
    mutableStateOf(false)
}
val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = spring(Spring.DampingRatioMediumBouncy, Spring.StiffnessHigh, 0.1.dp)
)
Box(
    Modifier
        .size(size)
        .background(Color.Blue)
        .clickable {
            big = !big
        }
)

效果如下所示

在这里插入图片描述

5.2 TweenSpec

可指定规定时间完成动画,通过tween进行调用。

可以使用Easing来可以控制动画的节奏。

5.2.1 TweenSpec的参数

  • durationMillis : 动画执行时长

  • delayMillis : 动画延迟多久执行

  • easing : 用来控制动画的节奏

5.2.2 Easing

在这里插入图片描述

5.2.3 代码示例

这段代码指定了在2秒时间匀速线性地完成动画

var big by remember {
    mutableStateOf(false)
}
val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = tween(2000, easing = LinearEasing)
)
Box(
    Modifier
    .size(size)
    .background(Color.Blue)
    .clickable {
        big = !big
    }
)

效果如下所示
在这里插入图片描述

5.3 RepeatableSpec / InfiniteRepeatableSpec

  • RepeatableSpec : 可循环播放的动画,需要包裹其他AnimateSpec
  • InfiniteRepeatableSpec: 无限循环动画,需要包裹其他AnimateSpec

repeatable/infiniteRepeatable不支持spring,因为一个循环运动的弹簧是违背物理规律的

5.3.1 代码示例

这段代码使用RepeatableSpec,循环播放动画5

val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = repeatable(
        iterations = 5,
        tween(2000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    )
)

这段代码使用InfiniteRepeatableSpec,无限循环播放动画

val size by animateDpAsState(
    targetValue = if (big) 300.dp else 50.dp,
    animationSpec = infiniteRepeatable(
        tween(2000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    )
)

6. Animatable

AnimatableAndroid Compose动画的底层API

  • animateDpAsState内部使用Animatable实现
  • Animatable具有更高的可定制性
  • 如果animateDpAsState能够满足需求,就用animateDpAsState就行了,否则才去使用Animatable

6.1 Animatable的参数

  • initialValue : 初始值
  • typeConverter : 转换到什么单位 (比如Dp)
  • visibilityThreshold : 当动画到达这个阈值会立即停止

6.2 代码示例

通过调用animatable.animateTo实现动画的过渡,animateTo必须要在LaunchedEffect内部执行
这段代码的动画效果,和使用AnimateDpAsState的动画效果一致。

@Composable
private fun AnimatableTest() {
    var big by remember {
        mutableStateOf(false)
    }
    var size = if (big) 300.dp else 50.dp
    val animatable = remember {
        Animatable(size, Dp.VectorConverter)
    }
    LaunchedEffect(key1 = big) {
        animatable.animateTo(size)
    }
    Box(
        Modifier
            .size(animatable.value)
            .background(Color.Blue)
            .clickable {
                big = !big
            }
    )
}

效果如下所示
在这里插入图片描述

6.3 Compose里的协程 : LaunchedEffect

LaunchedEffectCompose里的协程
为什么要专门为Compose出一个专门的协程呢 ?
因为Compose中每次状态改变,Compose进行重组更改UI的时候,就会去重新执行相应的代码块

  • 如果使用原本的协程,每次Compose的重组都会执行该代码,这肯定是不行的
  • LaunchedEffect专门针对Compose重组的这个特定,做了特定的处理,只有其传入的key值发生变化的时候,才会去执行
LaunchedEffect(key1 = big) {
    animatable.animateTo(size)
}

7. 为什么使用animateDpAsState,就可以实现动画渐变的效果 ?

其实我们点击AnimateDpAsState内部的代码,可以发现其也是调用Animatable来实现的。

val animatable = remember {
    Animatable(size, Dp.VectorConverter)
}
LaunchedEffect(key1 = targetValue) {
    animatable.animateTo(size)
}

也就是说,animateDpAsState内部会去启动一个协程,通过animatable.animateTo会在某一个时间段内,自动完成从当前值到目标值的计算,并且实时将该值通知给Compose重组,从而实现页面界面的更新,达到动画渐变的效果。

而什么时候触发协程呢 ? 就是当协程传入的这个keytargetValue发生变化的时候。

8. 小结

到这里,我们就把Compose的重点API都讲完了,但是要想真正入门Compose动画,还需要上手亲自上手去敲一下代码,真正实践下才能真的掌握。在此基础上,如果想要看更详细的Compose动画,欢迎看我的Compose动画 专栏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/26813.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT新突破:打造自己的智能机器人控制系统

&#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#xff1a;Zeeland&#x1f4da; Github主页: Undertone0809 (Zeeland) (github.com)&…

【论文速览】根据人脑fMRI信号重建图像 Image Reconstruction from human brain activity

文章目录 前言文章一研究背景主要方法部分实验结果总结与思考参考资料 文章二研究背景主要方法部分实验结果总结与思考 前言 人类的视觉神经系统对于真实世界的视觉刺激有着非凡的感知与理解能力&#xff0c;比如我们能够准确地识别物体距离和三维几何关系等&#xff0c;这是当…

三维数字沙盘交互大数据可视化GIS地理信息系统第十课

三维电子沙盘交互无人机倾斜摄影大数据可视化GIS地理信息系统第十课 设置system.ini 如下内容 Server122.112.229.220 userGisTest Passwordchinamtouch.com 该数据库中只提供 成都市火车南站附近的数据请注意&#xff0c;104.0648,30.61658 在SDK中自带了一个自定义的基础面…

pycharm和virtualBox虚拟机的安装(包括本地环境和远程环境配置)

目录 一、安装时需要的软件二、安装virtualBox三、安装pycharm四、创建pycharm本地环境五、创建pycharm远程环境 一、安装时需要的软件 Pycharm&#xff0c;jetbrains-agent-latest破解包&#xff08;破解pycharm&#xff09;;镜像文件ubuntu20&#xff0c;虚拟机virtualBox …

Zellij – 颜值爆表,比tmux、screen更好用的多窗口终端

如果你曾经使用过多窗口终端&#xff0c;如tmux、screen&#xff0c;那么你可能对Zellij上手会更快。下面将介绍这个惊艳出众的多窗口终端利器。 一、Zellij 特点 Zellij最大的特点是支持插件&#xff0c;与WebAssembly编译兼容。与screen和tmux相比&#xff0c;Zellij是以细…

Linux 之Python 定制篇-APT 软件管理和远程登录

Linux 之Python 定制篇-APT 软件管理和远程登录 apt 介绍 apt 是Advanced Packaging Tool 的简称&#xff0c;是一款安装包管理工具。在Ubuntu 下&#xff0c;我们可以使用apt 命令进行软件包的安装、删除、清理等&#xff0c;类似于Windows 中的软件管理工具。 unbuntu 软件…

LVS-DR负载群集的优势和部署实例(我们都会在各自喜欢的事情里变得可爱)

文章目录 一、DR模式数据包流向分析二、DR模式的特点三、DR模式中需要解决的问题问题1解决方式 问题2解决方式 四、LVS-DR部署实例1.配置NFS共享存储器2.配置节点web服务&#xff08;两台的配置相同&#xff09;3.配置LVS负载调度器 一、DR模式数据包流向分析 1.Client 客户端…

《计算机网络——自顶向下方法》精炼——3.7(2)

读书有三到&#xff1a;谓心到&#xff0c;眼到&#xff0c;口到。——明朱熹 文章目录 对链接吞吐量的简化描述高带宽路径的TCP公平性 对链接吞吐量的简化描述 为了简化对一条TCP连接吞吐量的描述&#xff0c;我们首先忽略连接过程中处于慢启动状态的时间&#xff0c;因为这一…

chatgpt赋能python:Python将yyyymmdd转换成yyyy-mm-dd的方法

Python将yyyymmdd转换成yyyy-mm-dd的方法 Python语言不仅易于学习&#xff0c;而且是一种功能强大的语言&#xff0c;广泛应用于数据分析、人工智能和Web开发等领域。在实际开发过程中&#xff0c;我们经常遇到需要将日期格式转换为其他格式的需求。本文将介绍如何使用Python将…

Nginx rewrite

目录 一、location 1.location 匹配规则介绍 2. 实际网站使用中匹配规则 2.1第一个必选规则 2.2第二个必选规则是处理静态文件请求&#xff0c;这是nginx作为http服务器的强项 2.3第三个规则就是通用规则 3.location 匹配规则演示 2.1一般前缀匹配 2.2正则匹配 2.3正则…

电池状态估计 | Matlab实现利用卡尔曼滤波器估计电池充电状态

文章目录 效果一览文章概述研究内容程序设计参考资料效果一览 文章概述 电池状态估计 | Matlab实现利用卡尔曼滤波器估计电池充电状态 研究内容 目前,常用的电池模型有:数

斐波那契数列题解(非递归c++方法实现)

在做信奥赛&#xff08;信息学奥赛&#xff09;中的for循环题目时&#xff0c;有一道斐波那契数列&#xff0c;想到的第一个方法是使用递归求解&#xff1b;因为以往题目最多使用的就是递归形式&#xff0c;但鉴于该题目在for循环题目堆&#xff0c;所以就思考了一些新方法&…

仙境传说RO:添加限购物品刷新物品库存教程

仙境传说RO&#xff1a;添加限购物品刷新物品库存教程 大家好我是艾西&#xff0c;在游戏中我们会有普通的基础装备那么必然就会有到顶的套装&#xff0c;往往可能一套到顶的套装就可能霸服。那么就需要GM去做游戏的设定以及限制&#xff0c;上一篇文章中我给大家讲述了如果创…

RabbitMQ的基本概念

目录 1、MQ 的基本概念 1.1 MQ概述 1.2 MQ 的优势和劣势 1.3 MQ 的优势 1. 应用解耦 2. 异步提速 3. 削峰填谷 小结: 1.4 MQ 的劣势 1.5 常见的 MQ 产品 1.6 RabbitMQ 简介 1.7 JMS 1、MQ 的基本概念 1.1 MQ概述 MQ全称 Message Queue&#xff08;消息队列&#…

火山引擎DataLeap的Catalog系统搜索实践(三):Learning to rank与后续工作

Learning to rank Learning to rank主要分为数据收集&#xff0c;离线训练和在线预测三个部分。搜索系统是一个Data-driven system&#xff0c;因此火山引擎DataLeap的Catalog系统设计之初就需要考虑数据收集。收集的数据可以用来评估和提升搜索的效果。数据收集和在线预测前面…

Augmentation Matters:一种简单而有效的半监督语义分割方法(CVPR2023)

文章目录 Augmentation Matters: A Simple-yet-Effective Approach to Semi-supervised Semantic Segmentation摘要本文方法Random Intensity-based AugmentationsAdaptive Label-aided CutMix 实验结果 Augmentation Matters: A Simple-yet-Effective Approach to Semi-superv…

【C语言】C预处理器(宏、文件包含、条件编译...)

一、C语言编译的预处理阶段1.1 C语言的编译过程1.2 C语言编译的预处理 二、C语言 宏2.1替换常量2.2函数宏2.3 字符串化和连接&#xff1a;#和##2.4 变参宏 三、文件包含&#xff1a;#include3.1 写法3.2 头文件的作用——声明3.3 头文件和extern 、static 四、 其他指令4.1 #un…

路径之谜 2016年国赛 深度优先搜索

目录 解题思路 AC代码&#xff1a; 题目描述 小明冒充 XX 星球的骑士&#xff0c;进入了一个奇怪的城堡。 城堡里边什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡地面是 nn 个方格。如下图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。可以横向或纵向…

公司新来一00后,真让人崩溃...

2022年已经结束结束了&#xff0c;最近内卷严重&#xff0c;各种跳槽裁员&#xff0c;相信很多小伙伴也在准备今年的金九银十的面试计划。 在此展示一套学习笔记 / 面试手册&#xff0c;年后跳槽的朋友可以好好刷一刷&#xff0c;还是挺有必要的&#xff0c;它几乎涵盖了所有的…

Executor框架的两级调度模型

Executor框架的两级调度模型 在HotSpot VM的线程模型中Java线程&#xff08;java.lang.Thread&#xff09;被一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程&#xff1b;当该Java线程终止时&#xff0c;这个操作系统线程也会被回收。操作系统会调度…