手把手教你写 Compose 动画 -- 更强大的多组件切换动画 API:AnimatedContent_compose animatedcontent-程序员宅基地

技术标签: Jetpack Compose  android jetpack  

Jetpack Compose 提供了一系列功能强大且可扩展的 API,可用于在应用界面中轻松实现各种动画效果。这一系列文章会逐个介绍所有的动画 API,通过最直观的 Demo 示例,手把手教你怎么写动画以及带你了解动画背后的原理。


手把手教你写 Compose 动画 - - 状态转移型动画 API:animate*AsState()

手把手教你写 Compose 动画 - - 流程定制型动画 API:Animatable()

手把手教你写 Compose 动画 - - 讲的不能再细的 AnimationSpec 动画规范

手把手教你写 Compose 动画 - - 过渡动画 API:Transition

手把手教你写 Compose 动画 - - 显示与消失 API:AnimatedVisibility

手把手教你写 Compose 动画 - - 简单页面切换动画 API:Crossfade

手把手教你写 Compose 动画 - - 更强大的多组件切换动画 API:AnimatedContent

手把手教你写 Compose 动画 - - 组件大小变化 API:animateContentSize



动画图表


在每一篇文章开头,我都会放一张 Compose 动画 API 的图表,以便你有最直观的感受。

在这里插入图片描述


AnimatedContent()


AnimatedContent 用来控制多个组件的入场和出场,同时还能对入场和出场效果做定制,相当于是 AnimatedVisibility 和Crossfade 的结合,AnimatedContent 出入场动画效果的尺寸是渐变的,这个是区别于 Crossfade 的一个点。


还是那句话:探索新技术的最佳方式是尝试它们,我们把 Crossfade 那个代码示例搬过来:

class MainActivity : ComponentActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)

        setContent {
    
            cf()
        }
    }
}

@Composable
fun cf() {
    
    var shown by remember {
     mutableStateOf(true) }

    Column (
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
    
        Crossfade(targetState = shown, animationSpec = tween(3000), label = "Crossfade") {
    
            if (it) {
    
                showCR7_1()
            } else {
    
                showCR7_2()
            }
        }

        Button(
            onClick = {
     shown = !shown}
        ) {
    
            Text(text = "切换")
        }
    }
}

@Composable
fun showCR7_1() {
    
    Image(
        painter = painterResource(R.drawable.cr7),
        contentDescription = null,
        modifier = Modifier
            .size(90.dp)
            .clip(shape = CircleShape)
    )
}

@Composable
fun showCR7_2() {
    
    Image(
        painter = painterResource(R.drawable.cr7_2),
        contentDescription = null,
        modifier = Modifier
            .size(60.dp)
            .clip(shape = CircleShape)
    )
}

看下效果:

在这里插入图片描述

Crossfade 是没法对尺寸变换进行渐变的,那么我们现在来看看 AnimatedContent 的效果。

@Preview
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun cf() {
    
    var shown by remember {
     mutableStateOf(true) }

    Column (
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
    
        Spacer(modifier = Modifier.height(100.dp))
        AnimatedContent(targetState = shown, label = "AnimatedContent") {
    
            if (it) {
    
                showCR7_1()
            } else {
    
                showCR7_2()
            }
        }

        Button(
            onClick = {
     shown = !shown}
        ) {
    
            Text(text = "切换")
        }
    }
}

看下效果:


在这里插入图片描述


Perfect!效果要比 Crossfade 好多了,不仅图片淡入淡出,而且尺寸变化也是渐变的

现在我们再通过动画预览看下效果:


在这里插入图片描述


可以很清晰的发现,这里有好几种入场和出场的动画效果,我们之前研究过 AnimatedVisibility,它是对单个内容的出现和消失添加动画效果的,但我们这里的场景是对多个内容(多个组件)的出现和消失添加动画效果,而且它的动画效果很明显用到了 AnimatedVisibility,是怎么做的呢?

这就涉及到 AnimatedContent 函数定义里面的一个核心参数了,我们来看一下:

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

我们发现它有一个核心参数:transitionSpec,而且需要一个 ContentTransform 对象。


ContentTransform


我们看下 ContentTransform 的构造函数:

@ExperimentalAnimationApi
class ContentTransform(
    val targetContentEnter: EnterTransition,
    val initialContentExit: ExitTransition,
    targetContentZIndex: Float = 0f,
    sizeTransform: SizeTransform? = SizeTransform()
) {
    
    var targetContentZIndex by mutableStateOf(targetContentZIndex)
    var sizeTransform: SizeTransform? = sizeTransform
        internal set
}

它有四个核心参数,我们一个个分析下。

  1. targetContentEnter:目标组件入场动画

什么是目标组件的入场动画?对于我们这个例子来说,就是 showCR7_2() 这个可组合项。

在这里插入图片描述

这个入场动画在哪里设定的?我们之前看到过:

transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = {
    
    fadeIn(animationSpec = tween(220, delayMillis = 90)) +
        scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)) with
        fadeOut(animationSpec = tween(90))
},

我们来分析下这段代码:

⇒ fadeIn() + scaleIn() 不难理解,我们在 AnimatedVisibility 一文中讲过,这是淡入和缩放两个入场动画的合并。
⇒ fadeOut() 也不难理解,它是出场淡出的动画。

但是 with 是什么?

@ExperimentalAnimationApi
infix fun EnterTransition.with(exit: ExitTransition) = ContentTransform(this, exit)

原来这么简单:直接创建了 ContentTransform,并且你看到它的参数么?

this -- fadeIn(animationSpec = tween(220, delayMillis = 90)) +
        scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90))

exit -- fadeOut(animationSpec = tween(90))

所以更进一步:

class ContentTransform(
    val targetContentEnter: EnterTransition,
    val initialContentExit: ExitTransition,

---
targetContentEnter -- this -- fadeIn(animationSpec = tween(220, delayMillis = 90)) +
        scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90))

initialContentExit -- exit -- fadeOut(animationSpec = tween(90))

  1. initialContentExit:初始组件的出场动画

什么是目标组件的出场动画?对于我们这个例子来说,就是 showCR7_1() 这个可组合项。

在这里插入图片描述

  1. targetContentZIndex:控制 Z 轴高度,控制绘制顺序,靠上就遮盖别的组件

一般不用配置这个参数,因为默认入场的组件都会后绘制,出场的先绘制。

  1. sizeTransform:对尺寸渐变动画的配置

我不知道你看之前的动画的时候有没有发现一个疑问点,我们再来看下动画:

在这里插入图片描述

尺寸渐变的过程中,明显有裁切的效果,这个就是 sizeTransform 参数的原因。

@ExperimentalAnimationApi
class ContentTransform(
    val targetContentEnter: EnterTransition,
    val initialContentExit: ExitTransition,
    targetContentZIndex: Float = 0f,
    sizeTransform: SizeTransform? = SizeTransform() // 对尺寸渐变动画的配置
)

我们来看下 SizeTransform 的构造函数:

@ExperimentalAnimationApi
fun SizeTransform(
    clip: Boolean = true,
    sizeAnimationSpec: (initialSize: IntSize, targetSize: IntSize) -> FiniteAnimationSpec<IntSize> =
        {
     _, _ -> spring(visibilityThreshold = IntSize.VisibilityThreshold) }
): SizeTransform = SizeTransformImpl(clip, sizeAnimationSpec)

发现了没?SizeTransform 有两个参数:

  1. clip:是否裁切
  2. sizeAnimationSpec:动画曲线设置

动画曲线设置太熟了,我们不聊了,现在来看下不裁切会是什么样。

比如我们自己写一个 transitionSpec:

@Composable
fun cf() {
    
    var shown by remember {
     mutableStateOf(true) }

    Column (
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
    
        Spacer(modifier = Modifier.height(100.dp))
        AnimatedContent(targetState = shown,
            transitionSpec = {
     fadeIn(tween(1500)) with
                    fadeOut(tween(1500, delayMillis = 1500)) },
            label = "AnimatedContent") {
    
            if (it) {
    
                showCR7_1()
            } else {
    
                showCR7_2()
            }
        }

        Button(
            onClick = {
     shown = !shown}
        ) {
    
            Text(text = "切换")
        }
    }
}

看下动画效果:

在这里插入图片描述

现在我们把裁切关掉,像下面这么写:

transitionSpec = {
     fadeIn(tween(1500, delayMillis = 1500)) with
        fadeOut(tween(1500)) using SizeTransform(clip = false) },

using 是什么?

@ExperimentalAnimationApi
infix fun ContentTransform.using(sizeTransform: SizeTransform?) = this.apply {
    
    this.sizeTransform = sizeTransform
}

懂了吧,直接看效果:

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/pepsimaxin/article/details/134971480

智能推荐

Selenium 根据元素文本内容定位_selenium java根据文字定位-程序员宅基地

文章浏览阅读1.8k次。使用xpath定位元素时,有时候担心元素位置会变,可以考虑使用文本内容来定位的方式。例如图中的【评价】按钮,只有按钮文本没变,就可以定位到该元素。_selenium java根据文字定位

Linux中设置固定ip的方法_linux设置固定ip-程序员宅基地

这篇文章介绍了在Linux中设置固定IP的方法,包括使用ifconfig命令查看网络状态、使用ping命令检查网络连接、修改网络配置文件和管理防火墙。

在readthedocs上部署mkdocs文本报错AttributeError:module ‘jinja2‘ has no attribute ‘contextfilter‘解决_attributeerror: module 'jinja2' has no attribute '-程序员宅基地

文章浏览阅读700次。关于在readthedocs上部署mkdocs时遇到的问题_attributeerror: module 'jinja2' has no attribute 'contextfunction

tensorflow版本与cuda cuDNN版本对应使用_tensorflow2对应的cuda-程序员宅基地

文章浏览阅读4.3w次,点赞8次,收藏57次。tensorflow-gpu v1.9.0 |cuda9.0 | cuDNN7.1.4可行 | 备注:7.0.4/ 7.0.5/ 7.1.2不明确tensorflow-gpu v1.8.0 | cuda9.0 | cuDNN 不明确 | 备注:7.0.4/ 7.0.5/ 7.1.2/ 7.1.4tensorflow-gpu v1.7.0 | cuda9.0 | cuDNN 不..._tensorflow2对应的cuda

UVA 147 Dollars 完全背包-程序员宅基地

文章浏览阅读332次。题目不难,一个完全背包而已。只是需要注意,我用double直接读入莫名WA,看了别人的题解才发现要+上一个0.005,可是我依然无法理解为什么是这样。#include#include#includeusing namespace std;;const int maxm=30000;long long arr[maxm+10]{1},value[11]={5,10,20,50,100_uva 147

SSM框架学习笔记之SpringMVC异常处理机制_ssm框架抛出异常-程序员宅基地

文章浏览阅读135次。SpringMVC的异常处理机制1.异常处理的思路  系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。  系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理机制进行异常处理,如下图:2.异常处理两种方式(1)使用SpringMVC提供的简单异常处理器SimpleMappingExcep_ssm框架抛出异常

随便推点

Flutter网络请求_flutter provider 网络请求-程序员宅基地

文章浏览阅读2k次。这里主要分享我对于Flutter网络请求方面的内容目的目的很简单,需要完成项目中常规的Http的GET和POST请求服务端接口数据以完成页面部分展示逻辑方案主要实现方案有三种,一种是基于原生的HttpClient来实现,另外两种是基于第三方package来实现:http与diohttpClient原生方式主要基于dart:io库中的httpClient来实现:import 'dart:io';var httpClient = new HttpClient();该 client 支持常用_flutter provider 网络请求

java list 内存溢出_java内存溢出的2种情况-程序员宅基地

文章浏览阅读3.2k次,点赞2次,收藏4次。java程序员在面试经常被问到内存om之后如何处理,但是实际在工作中遇到此类问题的情况却又非常少。进过自己一番总结内存溢出主要分为2种:一、堆内存溢出 OutOfMemoryError从jvm的角度看发生的情况是:1、动态扩展的栈内存无法满足内存分配。2、建立新的线程没有足够内存创建栈。从编码角度看发生的情况是:1、内存中加载的数据量过于庞大,如一次从数据库取出过多数据;2、集合类中有对对象的引用..._java中怎么解决list内存溢出

Metamap Java Api 使用教程_metamap使用教程-程序员宅基地

文章浏览阅读5.4k次。Metamap(简称mm)是一个用于识别文本中包含的一体化医学语言系统概念的工具。官网地址:https://mmtx.nlm.nih.gov/mm java api允许通过java程序调用mm的映射引擎,进入mm java api页面(https://mmtx.nlm.nih.gov/JavaApi.shtml)使用mm java api之前需要下载以下工具包:1)_metamap使用教程

STM32F103VET6+keil5+STM32CubeMX 点亮LED灯_stm32f103vet stm32cube-程序员宅基地

文章浏览阅读2k次。STM32F103VET6+keil5+STM32CubeMX 点亮LED灯_stm32f103vet stm32cube

Apk Installer —— 一款Windows下自动关联APK文件且双击APK安装到任意安卓设备上的工具-程序员宅基地

文章浏览阅读5.2k次。Apk Installer介绍Apk Installer(原名:WSAInstallTool,自1.2.3.0版本后更名)是一款Windows下自动关联APK文件且双击APK安装到任意安卓设备上的工具。该软件可以显示Apk自身的图标,详细的权限列表等。支持Windows 11 安卓子系统,支持Windows 7、Windows 10 、Windows 11安装软件至任意安卓设备上。如软件无法运行,请安装.Net Framework 4.5.2。该软件主要服务于Windows 11 的Android子系_apk installer

MicroStation V8i简体中文版完全补丁安装教程(附安装包下载)_microstation v8i 下载-程序员宅基地

文章浏览阅读4.5w次,点赞20次,收藏18次。MicroStation是一款非常不错的二维和三维设计软件,由奔特力(Bentley)工程软件系统有限公司开发的一款软件。在CAD设计上该软件是和AutoCAD是齐名的软件,其专用的文件格式是DGN,当然该软件还兼容AutoCAD的DWG/DXF等格式,该软件的应用已经非常广泛,在建筑、土木工程、交通运输、加工工厂、离散制造业、政府部门、公用事业和电讯网络等领域都有使用到该软件。小编今天带来的是..._microstation v8i 下载