Compose 动画 (三) : AnimatedVisibility 从入门到深入

news2024/12/23 13:59:46

1. AnimatedVisibility 是什么

AnimatedVisibility可以实现Compose组件的显示和隐藏,并且可以指定显示/隐藏时候的动画效果。(EnterTransition/ExitTransition)
和 animateXxxAsState、animateContentSizeCrossfadeAnimatedContent 这几个API一起,都是Compose的高级别动画API,是比较易用的。

2. AnimatedVisibility 的基础使用

@Composable
fun AnimatedVisibilityPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        var visible by remember {
            mutableStateOf(true)
        }
        AnimatedVisibility(visible = visible) {
	        Image(
	            painter = painterResource(id = R.mipmap.photot1),
	            modifier = Modifier.width(300.dp),
	            contentDescription = null
	        )
	    }
        Spacer(modifier = Modifier.height(10.dp))
        Button(onClick = { visible = !visible }) {
            Text(text = "显示/隐藏")
        }
    }
}

看上去是不是很简单,只需要在Image外层包上AnimatedVisibility就可以了,显示效果如下
在这里插入图片描述
我们点进AnimatedVisibility的源码,可以看到如下代码

@Composable
fun ColumnScope.AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandVertically(),
    exit: ExitTransition = fadeOut() + shrinkVertically(),
    label: String = "AnimatedVisibility",
    content: @Composable AnimatedVisibilityScope.() -> Unit
){
    val transition = updateTransition(visible, label)
    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
}

可以发现其内部调用了updateTransition,该函数内部会返回Transition对象。

@Composable
fun <T> updateTransition(
    targetState: T,
    label: String? = null
): Transition<T> {
	//...省略...
}

Transition可管理一个或多个动画作为其子项,并在多个状态之间同时运行这些动画。
这个我们后续文章会讲到,现在先知道有这样一个概念就好。

3. 入场和出场效果

enterexit,用来配置入场/出场时候的动画效果。

默认的入场效果是 fadeIn() + expandVertically()
默认的出场效果是 fadeOut() + shrinkVertically()

3.1 EnterTransitionExitTransition支持的动画

enter的参数类型是EnterTransition,支持这些动画

  • fade: fadeIn
  • scale: scaleIn
  • slide: slideIn, slideInHorizontally, slideInVertically
  • expand: expandIn, expandHorizontally, expandVertically

exit的参数类型是EnterTransition

  • fade: fadeOut
  • scale: scaleOut
  • slide: slideOut, slideOutHorizontally, slideOutVertically
  • shrink: shrinkOut, shrinkHorizontally, shrinkVertically

可以看到EnterTransitionExitTransition支持的动画只有expandshrink命名上有区别,
其他都是对应的 fadeInfadeOutscaleInscaleOutslideInslideOut
expandshrink命名上做区分,是因为expand就是展开的意思,而shrink收缩的意思,它们其实就是相对应的。

3.1.1 fadeIn / fadeOut

fadeIn / fadeOut是淡出淡出的效果

我们依旧使用上面的那段代码

@Composable
fun AnimatedVisibilityPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        var visible by remember {
            mutableStateOf(true)
        }
        AnimatedVisibility(visible = visible) {
	        Image(
	            painter = painterResource(id = R.mipmap.photot1),
	            modifier = Modifier.width(300.dp),
	            contentDescription = null
	        )
	    }
        Spacer(modifier = Modifier.height(10.dp))
        Button(onClick = { visible = !visible }) {
            Text(text = "显示/隐藏")
        }
    }
}

先把图片部分抽取为一个Composable函数

@Composable
private fun MyImage() {
    Image(
        painter = painterResource(id = R.mipmap.photot1),
        modifier = Modifier.width(300.dp),
        contentDescription = null
    )
}

然后修改AnimatedVisibility,配置enterexit

AnimatedVisibility(
    visible = visible,
    enter = fadeIn(),
    exit = fadeOut()
) {
    MyImage()
}

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

3.1.2 scaleIn / scaleOut

scaleIn / scaleOut是缩放的效果

AnimatedVisibility(
    visible = visible,
    enter = scaleIn(),
    exit = scaleOut()
) {
    MyImage()
}

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

3.1.3 slideIn / SlideOut

slideIn / SlideOut是滑动的效果,这里进入的初始位置和退出的目标位置都设置为了(300,-150),所以会从右上角进入/退出

AnimatedVisibility(
    visible = visible,
    enter = slideIn(initialOffset = {
        IntOffset(300, -150) }
    ),
    exit = slideOut(targetOffset = {
        IntOffset(300,-150) }
    )
) {
    MyImage()
}

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

3.1.3.1 slideInVertically / slideOutVertically

slideInVertically / slideOutVertically是垂直方向滑动进入/退出

AnimatedVisibility(
    visible = visible,
    enter = slideInVertically(),
    exit = slideOutVertically()
) {
    MyImage()
}

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

3.1.3.2 slideInHorizontally / slideOutHorizontally

slideInHorizontally / slideOutHorizontally是从横向方向滑动进入/退出

AnimatedVisibility(
    visible = visible,
    enter = slideInHorizontally(),
    exit = slideOutHorizontally()
) {
    MyImage()
}

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

3.1.4 expandIn / shrinkOut

expandIn / shrinkOut展开/收缩的效果

AnimatedVisibility(
    visible = visible,
    enter = expandIn(),
    exit = shrinkOut()
) {
    MyImage()
}

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

3.1.4.1 expandVertically / shrinkVertically

expandVertically / shrinkVertically是从垂直方向展开/收缩

AnimatedVisibility(
    visible = visible,
    enter = expandVertically(),
    exit = shrinkVertically()
) {
    MyImage()
}

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

3.1.4.2 expandHorizontally / shrinkHorizontally

expandHorizontally / shrinkHorizontally是从横向方向展开/收缩

AnimatedVisibility(
    visible = visible,
    enter = expandHorizontally(),
    exit = shrinkHorizontally()
) {
    MyImage()
}

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

3.1.5 EnterTransitionExitTransition的源码

再来看下源码,入场动画EnterTransition和出场动画ExitTransition内部都有TransitionData变量

sealed class EnterTransition {
    internal abstract val data: TransitionData
    //...
}
sealed class ExitTransition {
    internal abstract val data: TransitionData
    //...
}

TransitionData即是可配置的动画参数,分别对应fadeslideexpand/shrinkscale

internal data class TransitionData(
    val fade: Fade? = null,
    val slide: Slide? = null,
    val changeSize: ChangeSize? = null,
    val scale: Scale? = null
)

我们可以发现 EnterTransition 和 ExitTransition 是 sealed class,密封类
其子类可以出现在定义 sealed class 的不同文件中,但不允许出现在与不同的 module 中,且需要保证 package 一致
这样既可以避免 sealed class 文件过于庞大,又可以确保第三方库无法扩展你定义的 sealed class,达到限制类的扩展目的

3.2 +号的作用

AnimatedVisibility源码的部分,入场(enter)和出场(exit)的配置,使用了+,这个加号是用来做什么的呢 ?

首先,+号是Kotlin的一个特性 : 重载运算符

我们点击这个+号,就可以跳转到它的源码

@Stable
operator fun plus(enter: EnterTransition): EnterTransition {
        return EnterTransitionImpl(
            TransitionData(
                fade = data.fade ?: enter.data.fade,
                slide = data.slide ?: enter.data.slide,
                changeSize = data.changeSize ?: enter.data.changeSize,
                scale = data.scale ?: enter.data.scale
            )
        )
    }

如果data不为空,就用data的值,否则用enter/exit的。
这里的data就是EnterTransitionExitTransition中的那个变量internal abstract val data: TransitionData(见 3.1 EnterTransitionExitTransition支持的动画部分)

所以fadeIn() + expandVertically()fadeIn()会赋值给TransitionDatafadeexpandVertically会赋值给changeSize

即 : 合并各个动画的效果

3.2.1 两个相同的动画会有什么效果

如果是两个相同的动画,比如fadeIn(initialAlpha = 0.3f) + fadeIn(initialAlpha = 0.5f)
根据源码中的这个规则 如果data不为空,就用data的值,否则用enter/exit的。可知 :
两个fade,会重叠了,导致后面那部分不会生效,等同于fadeIn(initialAlpha = 0.3f)+号左边优先级高

3.3 多种动画效果结合

我们来尝试下多种动画效果,使用+号合并之后的效果

val density = LocalDensity.current
AnimatedVisibility(visible = visible,
    enter = slideInVertically {
        //从顶部-200dp的位置开始滑入
        with(density) { -200.dp.roundToPx() }
    } + expandHorizontally(
        //展开位置
        expandFrom = Alignment.End
    ) + fadeIn(
        //从初始透明度0.3f开始淡入
        initialAlpha = 0.3f
    ),
    exit = slideOutHorizontally() + shrinkHorizontally() + fadeOut()
) {
    MyImage()
}

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

4. 不同的作用域

4.1 AnimatedVisibility的作用域

AnimatedVisibility有好几种作用域,区别在于在这几种布局中,默认的入场动画出场动画是不同的

Column默认的出入场动画具有垂直展开,而Row的出入场动画具有横向展开Transition无前缀的出入场动画是展开/收缩

Column

fun ColumnScope.AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandVertically(),
    exit: ExitTransition = fadeOut() + shrinkVertically(),
    label: String = "AnimatedVisibility",
    content: @Composable AnimatedVisibilityScope.() -> Unit
)

Row

fun RowScope.AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandHorizontally(),
    exit: ExitTransition = fadeOut() + shrinkHorizontally(),
    label: String = "AnimatedVisibility",
    content: @Composable() AnimatedVisibilityScope.() -> Unit
)

Transition

@ExperimentalAnimationApi //实验的动画API
@Composable
fun <T> Transition<T>.AnimatedVisibility(
    visible: (T) -> Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    content: @Composable() AnimatedVisibilityScope.() -> Unit
) = AnimatedEnterExitImpl(this, visible, modifier, enter, exit, content)

无前缀

fun AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    label: String = "AnimatedVisibility",
    content: @Composable() AnimatedVisibilityScope.() -> Unit
)

4.2 使用不了AnimatedVisibility

如果我们在Column里面有个BoxBox里面又有AnimatedVisibility,会发现AnimatedVisibility会报错
在这里插入图片描述

我们把鼠标移到这个报红的地方,可以看到如下的提示

在这里插入图片描述
这边提示

'fun ColumnScope.AnimatedVisibility(visible: Boolean, modifier: Modifier = ..., enter: EnterTransition = ..., exit: ExitTransition = ..., label: String = ..., content: AnimatedVisibilityScope.() -> Unit): Unit' can't be called in this context by implicit receiver. Use the explicit one if necessary

注意最后一句
也就是说,不能使用隐式调用,而必须得用显式的。因为AnimatedVisibility有好几种作用域,ColumnScope和全局的作用域,IDE不知道该引用哪个了。

我们可以显示添加this@Column.,这样,就会引用Column的那个AnimatedVisibility
在这里插入图片描述
当然也可以包装一层Compose函数,使用全局作用域的AnimatedVisibility
在这里插入图片描述
这两种都是可行的

5. 其他

Compose 动画系列,后续持续更新,可以先关注
Compose 动画 (一) : animateXxxAsState 实现放大/缩小/渐变等效果
Compose 动画 (二) : 为什么animateDpAsState要用val ? MutableState和State有什么区别 ?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文详解像素、DPI、分辨率之间的关系

像素像素&#xff1a;是指在由一个数字序列表示的图像中的一个最小单位&#xff0c;称为像素。像素可以用一个数表示&#xff0c;比如一个“0.3兆像素”数码相机&#xff0c;它有额定30万像素&#xff1b;也可以用一对数字表示&#xff0c;例如“640x480显示器”&#xff0c;它…

单目标追踪——【相关滤波】C-COT原理与ECO基于C-COT的改进

目录C-COT&#xff1a;Continuous Convolution Operator Tracker文章侧重点连续卷积算子目标追踪框架初始化过滤器&#xff1a;追踪流程ECO文章侧重点因式卷积因子生成采样空间模型模型更新策略论文链接&#xff1a;C-COT&#xff1a;Beyond Correlation Filters: Learning Con…

【MySQL】表连接

一、为什么要学习 因为不合理的使用连接会导致慢查询 二、什么是连接 参与连接的表叫做 连接表&#xff0c; 连接就是把 各个连接表 进行的组合 &#xff08;笛卡儿积&#xff09;加入结果集并返回 三、连接查询 如何只是对表进行大量的连接&#xff0c;笛卡儿积作用得到的…

ChatGPT会取代程序员么?今天让ChatGPT写了个程序,感觉离失业不远了

文章目录ChatGPT会取代程序员么&#xff1f;今天让ChatGPT写了个程序&#xff0c;感觉离失业不远了问题&#xff1a;保存和ChatGPT的聊天记录对话实录以及吐槽1. 把当前页面转成markdown格式的方法2. 用油猴子可以实现么&#xff1f;3. 编写一段油猴子代码&#xff0c;实现刚才…

Redis十大类型——Set与Zset常见操作

Redis十大类型——Set与Zset常见操作Set命令操作简列基本操作展示删除移动剪切集合运算Zset基本操作简列添加展示反转按分数取值获取分数值删除分数操作下标操作如果我们对Java有所了解&#xff0c;相信大家很容易就明白Set&#xff0c;在Redis中也一样&#xff0c;Set的value值…

4、树(中篇)

前言:前节二叉树只能适用于静态查找,不能实现动态插入、删除等。如何解决以下两个问题: 静态查找与动态查找针对动态查找,数据如何组织?4.1 二叉搜索树 4.1.1 什么是二叉搜索树 二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树。 二叉搜索树:一棵…

Linux RS232驱动实验

目录 一、硬件分析 1、 RS232 原理图​编辑 2、RS485 原理图 3、GPS 原理图 二、设备树中添加设备节点 2、添加 uart3 节点 ​编辑 三、移植 minicom 1、移植 ncurses 2、移植 minicom 3、验证 4、minicom配置 四、测试 RS232 收发测试 串口是很常用的一个外设&…

SpringSecurity: 默认添加的15个Filter是怎么添加进去的?

总的流程分为两部分&#xff0c;一是先用Map把configurer收集起来&#xff0c;然后再把maper中所有的configurer应用到HttpSecurity对象。 其中的map位于AbstractConfiguredSecurityBuilder这个类。 private final LinkedHashMap<Class<? extends SecurityConfigurer&l…

信息安全与数学基础-笔记-⑤原根与阶

知识目录原根一些求原根的定理↓ordm(a)ord_m(a)ordm​(a) | ϕ(m)\phi(m)ϕ(m)原根和阶的关系原根 什么是原根&#xff1f; 设 &#xff08;a,m&#xff09; 1, 并且aea^eae 三 1 (mod m) &#xff0c;则我们称&#xff1a;ordmord_mordm​(a) e&#xff0c;ord也叫做群论&am…

React组件性能优化若干问题

react组件的性能优化的核心是减少渲染真实DOM节点的频率&#xff0c;减少Virtual DOM比对的频率。组件卸载前执行清理操作在组件中为window 注册的全局事件&#xff0c;以及定时器&#xff0c;在组件卸载前要清理掉。防止组件卸载后继续执行影响应用性能。import React from re…

分享十个前端Web3D可视化框架附地址

Three.js&#xff1a;Three.js是一个流行的3D库&#xff0c;提供了大量的3D功能&#xff0c;包括基本几何形状、材质、灯光、动画、特效等。它是一个功能强大、易于使用的框架&#xff0c;广泛用于Web3D可视化应用程序的开发。Three.js&#xff1a;https://threejs.org/Babylon…

【强烈建议收藏:MySQL面试必问系列之索引专题】

一.知识回顾 前面的文章我们一起学习了数据库的事务、事务以及并发来的问题、数据库锁机制、数据库中CURD的SQL语句底层执行流程、数据库SQL优化专题&#xff0c;如果你一步一步的跟下来&#xff0c;一定会帮助你建立一个知识体系。接下来我们再一起学习数据库索引专题&#x…

Toponogov 比较定理及其应用

1. Toponogov 比较定理的背景来源 Victor Andreevich Toponogov&#xff08;1930-2004&#xff09; 是苏联数学家&#xff0c;Toponogov 比较定理是他的博士论文题目&#xff0c;在1958年答辩。他证明这个定理是为了用于证明截面曲率假设下的分裂定理和最大直径定理&#xff0…

具有精密内部基准的 DACx0502 简介及驱动应用示例

DACx0502 说明 16 位 DAC80502、14 位 DAC70502 和 12 位DAC60502 (DACx0502) 数模转换器 (DAC) 均为具有电压输出的高精度、低功耗器件。 DACx0502 线性度小于 1LSB。凭借高精度和微型封装特性&#xff0c;DACx0502 非常适合以下 应用&#xff1a; 增益和失调电压校准、电流…

计算机网络协议详解(二)

文章目录&#x1f525;HTTP协议介绍&#x1f525;HTTP协议特点&#x1f525;HTTP协议发展和版本&#x1f525;HTTP协议中URI、URL、URN&#x1f525;HTTP协议的请求分析&#x1f525;HTTP协议的响应分析&#x1f525;MIME类型&#x1f525;HTTP协议介绍 HTTP协议介绍 什么是超…

反转链表(精美图示详解哦)

全文目录引言反转链表题目描述与思路实现总结引言 在学习了单链表的相关知识后&#xff0c;尝试实现一些题目可以帮助我们更好的理解单链表的结构以及对其的使用。 从这篇文章开始&#xff0c;将会介绍一些编程题来帮助我们更好的掌握单链表&#xff1a; 分别是反转链表、链表…

大数据技术之Hadoop集群配置

作者简介&#xff1a;大家好我是小唐同学(๑>؂<๑&#xff09;&#xff0c;好久不见&#xff0c;为梦想而努力的小唐又回来了&#xff0c;让我们一起加油&#xff01;&#xff01;&#xff01; 个人主页&#xff1a;小唐同学(๑>؂<๑&#xff09;的博客主页 目前…

SpringBoot三种方法实现定时发送邮件的案例

前言 小编我将用CSDN记录软件开发之路上所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01;也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&#xff0c;让我们共…

了解java

#常见编程语言介绍 C语言 C语言 java语言 javaScript语言 PHP语言 python语言Object-C和Swift语言 C# &#xff08;c sharp&#xff09;语言 Kotlin语言 Go语言 Basic语言 #JAVA的发展 起源于1991年SUN公司GREEN项目&#xff0c;1996年JDK1.0正式发布 后被Oracle公司收购&…

卷积神经网络CNN之ZF Net网络模型详解(理论篇)

1.背景 2. ZF Net模型结构 3. 改进优缺点 一、背景 ZF Net是用作者的名字命名的&#xff0c;Matthew D.Zeiler 和 Rob Fergus &#xff08;纽约大学&#xff09;&#xff0c;2013年撰写的论文&#xff1b; 论文原网址https://arxiv.org/abs/1311.2901 论文名&#xff1a;Vis…