Compose高级别API动画指南

前文讲了Compose中的低级别API动画,与之对应的,还有高级别API动画,同样也符合Material-Design规范。所有高级别动画 API 都是在低级别动画 API 的基础上构建而成,其对应关系如图:
image.gif
接下来就对其高级别API逐个分析:

AnimatedVisibility

即可见性动画,原Google官方文档对此API有实验性标记(可能后面更新就删除了或有其余更改,代码中要用ExperimentalAnimationApi标记),到Compose历经多个版本迭代,这些API依旧坚挺在此,估计后续也不会消失了。

可见性动画,主要是为其内容的出现、消失提供动画效果。先来看下其函数定义:

@ExperimentalAnimationApi
@Composable
fun AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
    val transition = updateTransition(visible)
    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
}

在原生View体系动画中,如果想实现隐藏淡入淡出放大缩小等复杂效果,可能需要alpha、scale、transition等动画一起配合实现。但在Compose中,仅通过AnimatedVisibility即可实现,其参数 enter(EnterTransition) 和 exit(ExitTransition)就可以自定义达到淡入淡出的过渡效果。visibile含义就是是否可见,这个没什么好说的。modifier是修饰符,可自定义控件各种属性。content则是指的对应的子控件。从这里可以看出,关键两个参数是EnterTransition和ExitTransition,我们先来了解下它俩。

EnterTransition

Compose对其提供动画函数如下:

fadeIn:从指定的起始alpha到1f淡入。alpha默认为0f,动画规格默认使用spring(spring相关解释在后续的自定义动画中)。

slideIn:从定义的起始偏移量到IntOffset(0,0)的滑动内容。可以通过配置控制滑动方向。正x值表示从右向左滑动,负x值表示从左向右滑动。类似地,正y值和负y值分别对应向上滑动和向下滑动。

expandIn:将显示内容的范围从返回的大小扩展到完整大小。可以控制先显示哪一部分内容。默认情况下,展示内容从IntSize(0,0)至完整大小的动画,从显示内容的右下角(或RTL布局中的左下角)逐渐扩展至显示整个内容。

expandHorizontally:将显示内容的范围从返回的宽度水平扩展到整个宽度。可以控制首先显示哪一部分内容。默认情况下,展示内容从0到全宽的动画,逐渐扩展到显示整个内容。

expandVertically:将显示内容的范围从返回的高度垂直扩展到整个高度。可以控制首先显示哪一部分内容。默认情况下,展示内容从0到全高的动画,首先显示底边,然后显示其余内容。

slideInHorizontally:从定义的起始偏移量到0水平滑动内容(以像素为单位)。可以通过配置控制滑动方向。正值表示从右向左滑动,负值表示从左向右滑动。

slideInVertically:从定义的起始偏移量到0垂直滑动内容(以像素为单位)。可以通过配置控制滑动方向。正值意味着向上滑动,负值意味着向下滑动。

注意,slideIn、slideInVertically、slideInVertically 这种同类型只能同时存在一个。

ExitTransition

同样,Compose对其也提供了一系列动画函数:

fadeOut:从完全不透明到目标alpha淡出效果。默认情况下,内容将淡出为完全透明,动画规格也默认使用spring(spring相关解释在后续的自定义动画中)。

slideOut:从IntOffset(0,0)到定义的目标偏移量的滑动效果。可以通过配置控制方向。x值为正表示从左向右滑动,x值为负表示从右向左滑动。类似地,正y值和负y值分别对应向下滑动和向上滑动。

shrinkOut:展示内容的范围从完整大小缩小到返回的大小的效果。可以控制范围缩小动画的方向。默认情况下,展示内容从完整大小至IntSize(0,0)的动画,并朝内容的右下角(或RTL布局中的左下角)缩小。

shrinkHorizontally:展示内容从整个宽度水平缩小到返回的宽度的效果。可以控制范围缩小动画的方向。默认情况下,剪辑范围从全宽到0设置动画,并朝内容的结尾缩小。

shrinkVertically:展示内容从整个高度垂直缩小到返回的高度消失的效果。可以控制缩小动画的方向。默认情况下,剪辑范围从全高到0设置动画,并朝内容的底部缩小。

slideOutHorizontally:从0到定义的目标偏移量水平滑动内容(以像素为单位)。可以通过配置控制滑动方向。正值表示向右滑动,负值表示向左滑动。

slideOutVertically:从0到定义的目标偏移量垂直滑动内容(以像素为单位)。可以通过配置控制滑动方向。正值表示向下滑动,负值表示向上滑动。

以上内容说起来很抽象,看以下实例:

fun showAnim() {
    var isShow by remember { mutableStateOf(true) }
    Column(
        Modifier.size(300.dp,300.dp),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { isShow = !isShow }
        ) {
            Text(text = if (isShow) "Hide" else "Show")
        }

        Spacer(Modifier.height(1.dp))

        AnimatedVisibility(
            visible = isShow,
            enter = slideInVertically() + fadeIn(initialAlpha = 0.1f),
            exit = slideOutVertically() + fadeOut(targetAlpha = 0.1f)
        ) {
            Image(
                painter = painterResource(id = R.drawable.icon_pdx),
                contentDescription = null,
                Modifier.fillMaxSize()
            )
        }
    }
}

其对应效果为:

AnimatedVisibility -1.gif

可以看出,这里有淡入淡出效果的同时,还有上滑下滑效果。通过这种 + 运算符组合多个 EnterTransition 或 ExitTransition 对象(且每个对象都可自定义可选参数和行为)的方式,达到各种效果。其余方法对应效果可以自行验证。

AnimatedContent

内容大小动画,可在内容根据目标状态发生变化时,为内容添加动画效果。其构造函数如下:

@ExperimentalAnimationApi
@Composable
fun <S> AnimatedContent(
    targetState: S,
    modifier: Modifier = Modifier,
    transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = {
        fadeIn(animationSpec = tween(220, delayMillis = 90)) with fadeOut(animationSpec = tween(90))
    },
    contentAlignment: Alignment = Alignment.TopStart,
    content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit
) {
    val transition = updateTransition(targetState = targetState, label = "AnimatedContent")
    transition.AnimatedContent(
        modifier,
        transitionSpec,
        contentAlignment,
        content
    )
}

这里可以看出,使用 lambda 参数并将动画反映到内容中(可能描述不太准确),默认情况下,初始内容淡出,然后目标内容淡入(即淡出后淡入)。例如以下示例:

fun showAnim() {
    var data by remember { mutableStateOf(0) }
    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Center,
        Alignment.CenterHorizontally
    ) {
        Button(onClick = { data++ }) {
            Text("change")
        }

        AnimatedContent(targetState = data,
        transitionSpec ={ slideInVertically({ fullHeight ->  fullHeight}) with
                slideOutVertically({height -> -height})+ fadeOut()
        }
        ) {
            Text(text = "${data}", fontSize = 36.sp)
        }
    }
}

对应的效果为:

AnimatedContent - 1.gif

以上可见,当 targetState(这里是data) 发生变化时,content 在我们设置的动画中完成切换。

animateContentSize

此方法主要为大小变化添加动画效果。其构造函数如下:

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring(),
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
): Modifier = composed(
    inspectorInfo = debugInspectorInfo {
        name = "animateContentSize"
        properties["animationSpec"] = animationSpec
        properties["finishedListener"] = finishedListener
    }
) {
    // TODO: Listener could be a fun interface after 1.4
    val scope = rememberCoroutineScope()
    val animModifier = remember(scope) {
        SizeAnimationModifier(animationSpec, scope)
    }
    animModifier.listener = finishedListener
    this.clipToBounds().then(animModifier)
}

不难看出此函数为Modefier的一个扩展函数,示例如下:

fun showAnim() {
    var size by remember { mutableStateOf(Size(100F, 100F)) }
    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Center,
        Alignment.CenterHorizontally
    ) {
        Box(
            Modifier
                .animateContentSize()
        ) {
            Image(
                painter = painterResource(id = R.drawable.icon_pdx),
                contentDescription = null,
                Modifier
                    .animateContentSize()
                    .size(size = size.height.dp)
            )
        }
        Spacer(Modifier.height(10.dp))
        Button(
            onClick = {
                size = if (size.height == 100F) {
                    Size(250F, 250F)
                } else {
                    Size(100F, 100F)
                }
            }
        ) {
            Text(if (size.height == 100F) "收缩" else "展开")
        }
    }
}

此部分代码通过变量size监听状态变化实现布局大小的动画效果:

AnimContentSize - 1.gif

Crossfade

此方法主要用于在两个布局之间添加淡入淡出动画,即布局切换动画。通过切换传递给 current 参数的值,使得淡入淡出动画切换内容。其构造函数如下:

@Composable
fun <T> Crossfade(
    targetState: T,
    modifier: Modifier = Modifier,
    animationSpec: FiniteAnimationSpec<Float> = tween(),
    content: @Composable (T) -> Unit
)

targetState和content,在这里,targetState是指定当前的布局状态,content是显示内容,Modifier修饰符在这是修饰Crossfade,animationSpec则是指定动画类型。例如以下示例:

fun showAnim() {
    var page by remember { mutableStateOf(1) }
    Column {
        Button(onClick = { page = if (page == 1) 2 else 1 }) {
            Text("变变变")
        }
        Crossfade(
            targetState = page,
            modifier = Modifier
                .size(600.dp)
                .background(if (page == 1) Color.White else Color.Blue),
            animationSpec = spring()
        ) { screen ->
            when (screen) {
                1 -> Text("Page White", fontSize = 100.sp, color = Color.Blue)
                2 -> Text("Page Blue", fontSize = 100.sp, color = Color.White)
            }
        }
    }
}

对应效果则为:

AnimCrossfade - 1.gif

看到这里,你应该发现了,其实套路都差不多,如果你对低级别API的动画效果还有兴趣,请点这里。下一篇即是Compose的自定义动画。

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

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

相关文章

【王道数据结构】【chapter5树与二叉树】【P159t12】

设一棵二叉树的结点结构为(LLINK,INFO,RLINK)&#xff0c;ROOT为指向该二叉树根结点的指针&#xff0c;p和q分别为指向该二叉树中任意两个节点的指针&#xff0c;试编写算法ANCESTOR(ROOT,p,q,r)&#xff0c;找到p和q的最近公共祖先结点r #include <iostream> #include &…

Linux第54步_根文件系统第1步_编译busybox并安装_然后添加“根文件系统”的库

学习编译busybox&#xff0c;并安装&#xff0c;然后添加“根文件系统”的库。有人说busybox构建根文件系统&#xff0c;只适合学习&#xff0c;不适合做项目。 1、了解ubuntu的根文件系统 根文件系统的目录名为“/”&#xff0c;就是一个斜杠。 1)、输入“cd /回车”&…

算法学习——LeetCode力扣二叉树篇7

算法学习——LeetCode力扣二叉树篇7 236. 二叉树的最近公共祖先 236. 二叉树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点…

加速创新如何先从创意管理开始?

文章详细介绍了什么是创意管理以及它在组织中的重要性和最佳实践。创意管理是指在组织内捕捉、组织、评估和实施创意的过程。它通过建立一个结构化的系统&#xff0c;从员工、客户或其他利益相关者那里收集创意&#xff0c;并系统地审查和选择最有前景的创意进行进一步的开发或…

《区块链公链数据分析简易速速上手小册》第8章:实战案例研究(2024 最新版)

文章目录 8.1 案例分析&#xff1a;投资决策支持8.1.1 基础知识8.1.2 重点案例&#xff1a;股票市场趋势预测准备工作实现步骤步骤1: 加载和准备数据步骤2: 特征工程步骤3: 训练模型步骤4: 评估模型 结论 8.1.3 拓展案例 1&#xff1a;基于情感分析的投资策略准备工作实现步骤步…

【王道数据结构】【chapter5树与二叉树】【P159t14】

设有一棵满二叉树&#xff08;所有结点值均不同&#xff09;&#xff0c;已知其先序序列为pre&#xff0c;设计一个算法求其后序序列post #include <iostream> #include <stack> #include <queue> #include<string.h> typedef struct treenode{char da…

读十堂极简人工智能课笔记02_选对路径与犯错

1. 符号人工智能 1.1. 在符号处理中&#xff0c;单词被当成遵循一套规则、互相关联的符号 1.2. 符号人工智能让计算机能用单词来思考 1.3. 符号人工智能是最早、最成功的人工智能形式之一 1.4. 20世纪初的时候&#xff0c;伯特兰罗素、库尔特哥德尔和大卫希尔伯特等数学家就…

训练深度学习模型的过程

深度学习的训练过程是指通过大量的数据来调整神经网络的参数&#xff0c;以使其能够对输入数据进行准确的预测或分类. 训练神经网络的步骤 损失函数&#xff08;Loss Function&#xff09;是一个性能指标&#xff0c;反映神经网络生成接近期望值的值的程度。 损失函数直观上就…

书生浦语大模型实战营-课程笔记(1)

模型应用过程&#xff0c;大致还是了解的。和之前实习做CV项目的时候比起来&#xff0c;多了智能体这个环节。智能体是个啥&#xff1f; 类似上张图&#xff0c;智能体不太清楚。感觉是偏应用而不是模型的东西&#xff1f; 数据集类型很多&#xff0c;有文本/图片/视频。所以…

Vulnhub靶机:DC3

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;DC3&#xff08;10.0.2.56&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/dc-32,312…

洛谷C++简单题小练习day11—字母转换,分可乐两个小程序

day11--字母转换--2.14 习题概述 题目描述 输入一个小写字母&#xff0c;输出其对应的大写字母。例如输入 q[回车] 时&#xff0c;会输出 Q。 代码部分 #include<bits/stdc.h> using namespace std; int main() { char n;cin>>n;cout<<char(n-32)<…

代码+视频基于R语言进行K折交叉验证

我们在建立数据模型后通常希望在外部数据验证模型的检验能力。然而当没有外部数据可以验证的时候&#xff0c;交叉验证也不失为一种方法。交叉验验证&#xff08;交叉验证&#xff0c;&#xff23;&#xff36;&#xff09;则是一种评估模型泛化能力的方法&#xff0c;广泛应用…

StarUML无法安装扩展的解决方案

StarUML无法安装扩展解决方案 版本&#xff1a;StarUML3.2.2 遇到问题 Unable to access the extension registry, Please try again later. 解决方案 第一步 https://docs.staruml.io/user-guide/managing-extensions#install-extension官网给了怎么手动安装扩展器的方法…

(三十八)大数据实战——Atlas元数据管理平台的部署安装

前言 Apache Atlas 是一个开源的数据治理和元数据管理平台&#xff0c;旨在帮助组织有效管理和利用其数据资产。为组织提供开放式元数据管理和治理功能 &#xff0c;用以构建其数据资产目录&#xff0c;对这些资产进行分类和管理&#xff0c;形成数据字典 。并为数据分析师和数…

反无人机系统技术分析,无人机反制技术理论基础,无人机技术详解

近年来&#xff0c;经过大疆、parrot、3d robotics等公司不断的努力&#xff0c;具有强大功能的消费级无人机价格不断降低&#xff0c;操作简便性不断提高&#xff0c;无人机正快速地从尖端的军用设备转入大众市场&#xff0c;成为普通民众手中的玩具。 然而&#xff0c;随着消…

CFS三层靶机

参考博客&#xff1a; CFS三层内网靶场渗透记录【详细指南】 - FreeBuf网络安全行业门户 CFS三层靶机搭建及其内网渗透【附靶场环境】 | TeamsSix CFS三层网络环境靶场实战 - PANDA墨森 - 博客园 (cnblogs.com) CFS三层靶机实战--内网横向渗透 - 知乎 (zhihu.com) CFS靶机…

【Tomcat】:One or more listeners failed to start.报错解决方案

报错信息:One or more listeners failed to start. Full details will be found in the appropriate container log file. 具体就是web.xml此配置报错: 服务器启动错误Tomcat:One or more listeners failed to start.报错解决方案 IDEA:在使用IDEA运行SSM项目的时候 , Tomcat运…

【知识图谱--第四讲知识图谱的抽取与构建】

知识图谱的抽取与构建 实体识别与分类关系抽取与属性补全概念抽取事件识别与抽取 实体识别与分类 关系抽取与属性补全 概念抽取 事件识别与抽取

使用 Chainlit, Langchain 及 Elasticsearch 轻松实现对 PDF 文件的查询

在我之前的文章 “Elasticsearch&#xff1a;与多个 PDF 聊天 | LangChain Python 应用教程&#xff08;免费 LLMs 和嵌入&#xff09;” 里&#xff0c;我详述如何使用 Streamlit&#xff0c;Langchain, Elasticsearch 及 OpenAI 来针对 PDF 进行聊天。在今天的文章中&#xf…

anomalib1.0学习纪实

回顾&#xff1a;细分、纵深、高端、上游、积累、极致。 回顾&#xff1a;资本化&#xff0c;规模化&#xff0c;国际化&#xff0c;大干快上&#xff0c;小农思维必死无疑。 春节在深圳新地中央&#xff0c;学习anomalib1.0。 一、安装&#xff1a; 1、常规安装 采用的是…