Jetpack Compose动画实现原理详解

news2024/11/24 11:50:26

一、简介

Jetpack Compose是Google推出的用于构建原生界面的新Android 工具包,它可简化并加快 Android上的界面开发。Jetpack Compose是一个声明式的UI框架,随着该框架的推出,标志着Android 开始全面拥抱声明式UI开发。Jetpack Compose存在很多优点:代码更加简洁直观、应用开发效率显著提升、Kotlin API功能直观、预览工具强大等。

二、开发环境

打开工程,新建Empty Compose activity 模版,需要注意的是根目录下的build.gradle,相关的依赖com.android.tools.build和org.jetbrains.kotlin版本需要对应,否则可能出现出错的情形,这里使用的是:

dependencies {
	classpath "com.android.tools.build:gradle:7.0.0-alpha15"
	classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30"
}

这样就完成了项目的新建。

三、Jetpack Compose动画

Jetpack Compose提供了一些功能强大且可扩展的 API,可用于在应用界面中轻松实现各种动画效果。下文将会对Jetpack Compose Animations的常用方法进行介绍。

3.1 状态驱动动画:State

Jetpack Compose动画是通过对状态的监听,即监听状态值的变化,使UI能实现自动更新。可组合函数可以使用 remember或者 mutableStateOf监听状态值的变化。如果状态值是不变的,remember函数会在每次重新组合中保持该值;如果状态是可变的,它会在值发生变化的时候触发重组,mutableStateOf将得到一个MutableState对象,它是一个可观察类型。

这种重组是创建状态驱动动画的关键。利用重组,它们会在可组合组件的状态发生任何变化时被触发。Compose动画是由State驱动的,动画相关的API也较容易上手,能比较容易创造出漂亮的声明式动画。

3.2 可见性动画: AnimatedVisibility

首先看下函数定义:

@ExperimentalAnimationApi
@Composable
fun AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    initiallyVisible: Boolean = visible,
    content: @Composable () -> Unit
) {
    AnimatedVisibilityImpl(visible, modifier, enter, exit, initiallyVisible, content)
}

可以看出默认的动画是淡入放大、淡出收缩,实际中通过传入不同函数实现各种动效。

随着可见值的变化,AnimatedVisibility可为其内容的出现和消失设置动画。如下代码,可以通过点击Button,控制图片的出现和消失。

@Composable
fun AinmationDemo() {

    //AnimatedVisibility 可见动画
    var visible by remember { mutableStateOf(true) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { visible = !visible }
        ) {
            Text(text = if (visible) "Hide" else "Show")
        }

        Spacer(Modifier.height(16.dp))

        AnimatedVisibility(
            visible = visible,
            enter = slideInVertically() + fadeIn(),
            exit = slideOutVertically() + fadeOut()
        ) {
            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier.fillMaxSize()
            )
        }
    }
}

通过监听visible的变化,可实现图片的可见性动画,效果如小图所示;

3.3 布局大小动画:AnimateContentSize

先看下函数的定义:

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring(),
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
)

可以为布局大小动画设置动画速度和监听值。

由函数的定义可以看出这个函数本质上就Modefier的一个扩展函数。可以通过变量size监听状态变化实现布局大小的动画效果,代码如下:

//放大缩小动画 animateContentSize
    var size by remember { mutableStateOf(Size(300F, 300F)) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Spacer(Modifier.height(16.dp))

        Button(
            onClick = {
                size = if (size.height == 300F) {
                    Size(500F, 500F)
                } else {
                    Size(300F, 300F)
                }
            }
        ) {
            Text(if (size.height == 300F) "Shrink" else "Expand")
        }
        Spacer(Modifier.height(16.dp))

        Box(
            Modifier
                .animateContentSize()
        ) {
            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier
                    .animateContentSize()
                    .size(size = size.height.dp)
            )
        }
} //放大缩小动画 animateContentSize    var size by remember { mutableStateOf(Size(300F, 300F)) }​    Column(        Modifier            .fillMaxWidth()            .fillMaxHeight(),        Arrangement.Top,        Alignment.CenterHorizontally    ) {        Spacer(Modifier.height(16.dp))​        Button(            onClick = {                size = if (size.height == 300F) {                    Size(500F, 500F)                } else {                    Size(300F, 300F)                }            }        ) {            Text(if (size.height == 300F) "Shrink" else "Expand")        }        Spacer(Modifier.height(16.dp))​        Box(            Modifier                .animateContentSize()        ) {            Image(                painter = painterResource(id = R.drawable.pikaqiu),                contentDescription = null,                Modifier                    .animateContentSize()                    .size(size = size.height.dp)            )        }}

通过Button的点击,监听size值的变化,利用animateContentSize()实现动画效果,具体动效如下图所示:

3.4布局切换动画: Crossfade

Crossfade可以通过监听状态值的变化,使用淡入淡出的动画在两个布局之间添加动画效果,函数自身就是一个Composable,代码如下:

//Crossfade 淡入淡出动画
    var fadeStatus by remember { mutableStateOf(true) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { fadeStatus = !fadeStatus }
        ) {
            Text(text = if (fadeStatus) "Fade In" else "Fade Out")
        }

        Spacer(Modifier.height(16.dp))

        Crossfade(targetState = fadeStatus, animationSpec = tween(3000)) { screen ->
            when (screen) {
                true -> Image(
                    painter = painterResource(id = R.drawable.pikaqiu),
                    contentDescription = null,
                    Modifier
                        .animateContentSize()
                        .size(300.dp)
                )
                false -> Image(
                    painter = painterResource(id = R.drawable.pikaqiu2),
                    contentDescription = null,
                    Modifier
                        .animateContentSize()
                        .size(300.dp)
                )
            }
        }

    }

同样通过监听fadeStatus的值,实现布局切换的动画,具体的动效如图所示:

3.5单个值动画:animate*AsState

为单个值添加动画效果。只需提供结束值(或目标值),该 API 就会从当前值开始向指定值播放动画。

Jetpack Compose 提供了很多内置函数,可以为不同类型的数据制作动画,例如:animateColorAsState、animateDpAsState、animateOffsetAsState等,这里将介绍下animateFooAsState的使用,代码如下:

//animate*AsState 单个值添加动画
    var transparent by remember { mutableStateOf(true) }
    val alpha: Float by animateFloatAsState(if (transparent) 1f else 0.5f)

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { transparent = !transparent }
        ) {
            Text(if (transparent) "Light" else "Dark")
        }

        Spacer(Modifier.height(16.dp))

        Box {

            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier
                    .animateContentSize()
                    .graphicsLayer(alpha = alpha)
                    .size(300.dp)
            )
        }
}


动画效果如下图所示:

3.6 组合动画:updateTransition

Transition 可同时追踪一个或多个动画,并在多个状态之间同步这些动画。具体的代码如下:

var imagePosition by remember { mutableStateOf(ImagePosition.TopLeft) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Spacer(Modifier.height(16.dp))

        val transition = updateTransition(targetState = imagePosition, label = "")
        val boxOffset by transition.animateOffset(label = "") { position ->
            when (position) {
                ImagePosition.TopLeft -> Offset(-60F, 0F)
                ImagePosition.BottomRight -> Offset(60F, 120F)
                ImagePosition.TopRight -> Offset(60F, 0F)
                ImagePosition.BottomLeft -> Offset(-60F, 120F)
            }
        }
        Button(onClick = {
            imagePosition = ChangePosition(imagePosition)
        }) {
            Text("Change position")
        }
        Box {

            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier
                    .offset(boxOffset.x.dp, boxOffset.y.dp)
                    .animateContentSize()
                    .size(300.dp)
            )
        }
}

其中,ImagePosition、ChangePosition分别为定义的枚举类、自定义函数。

enum class ImagePosition {
    TopRight,
    TopLeft,
    BottomRight,
    BottomLeft
}

fun ChangePosition(position: ImagePosition) =
    when (position) {
        ImagePosition.TopLeft -> ImagePosition.BottomRight
        ImagePosition.BottomRight -> ImagePosition.TopRight
        ImagePosition.TopRight -> ImagePosition.BottomLeft
        ImagePosition.BottomLeft -> ImagePosition.TopLeft
    }

动画的如下图所示:

四、结语

Jetpack Compose 已将动画简化到只需在我们的可组合函数中创建声明性代码的程度,只需编写希望 UI 动画的方式,其余部分由 Compose 管理。最后,这也是是 Jetpack Compose 的主要目标:创建一个声明式 UI 工具包来加速应用程序开发并提高代码可读性和逻辑性。

Jetpack Compose提供的声明式UI工具包,能做到使用更少的代码实现更多的功能,且代码的可读性和逻辑性也大大提高了。

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

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

相关文章

【数据结构】红黑树封装map和set

文章目录 1.前置知识2.结构的改写与封装2.1 map和set的结构框架2.2 RBTreeNode结构的改写2.3 RBTree结构改写&#xff08;仿函数的引入&#xff09; 3. 迭代器3.1 RBTree的迭代器3.2 map和set的迭代器封装 4. 插入的改写和operatorp[]的重载4.1 insert的改写4.2 map::operator[…

【2023 · CANN训练营第一季】进阶班 应用开发深入讲解→DVPP

1 数据预处理概述 1.1 典型使用场景 受网络结构和训练方式等因素的影响&#xff0c;绝大多数神经网络模型对输入数据都有格式上的限制。在计算视觉领域&#xff0c;这个限制大多体现在图像的尺寸、色域、归一化参数等。如果源图或视频的尺寸、格式等与网络模型的要求不—致时…

pytest-编写插件

pytest 0 、文档1、钩子函数分类1.4 测试运行钩子 2、本地编写插件&#xff1a;conftest.py3、外部插件&#xff1a;setuptools4、实战 0 、文档 官方文档 中文文档 1、钩子函数分类 pytest中的钩子函数按功能一共分为6类&#xff1a;引导钩子&#xff0c;初始化钩子、用例收…

rtl仿真器-incisive安装和测试

需要的文件 安装文件 incisive : http://pan.baidu.com/s/1dFC9KZn 提取码 k3cb path: license: IScape: 安装的图形界面 IScape下载链接: https://pan.baidu.com/s/1FvpOto5fAIRjQARcbMbjZQ 密码: k1cb 目录结构 需要四个目录 安装目录:INCISIVE151 path 存放解密工具 l…

强化学习路线规划之深度强化学习

学到如今&#xff0c;我实在明白了一个至关重要的东西&#xff0c;那就是目标很重要&#xff0c;有了清晰的目标我们就知道该做什么&#xff0c;不至于迷茫&#xff0c;否则每天都在寻找道路。所以我一直在规划这样一条道路&#xff0c;让想学习的人可以抛下不知道该怎么做的顾…

在Notion AI 中轻松打造您的AI私人助理,提供卓越的工作体验(二)

大家好&#xff0c;我是瓜叔。 notion AI在工作和生活场景中的应用 我们先来看"总结"功能。 这边有一篇文章叫做学习编码的好处。导入到nation https://www.likecs.com/show-203992587.html 导入方法详见上一篇文章&#xff1a;在Notion AI 中轻松打造您的AI私人助理…

VMware快照:简化虚拟化环境管理与数据保护

引言&#xff1a; 在虚拟化环境中&#xff0c;数据保护和灵活性是至关重要的。VMware快照作为一项强大的功能&#xff0c;为虚拟机管理者提供了便利和安全性。本文将介绍VMware快照的使用&#xff0c;以及它为用户带来的几个关键优势。 VMware快照是一项重要的功能&#xff0c…

Threejs进阶之十五:在Thereejs 使用自定义shader

目录 最终效果什么是 ShaderShaderMaterial类常用属性uniforms属性vertexShader属性fragmentShader属性 代码实现新建ShaderView.vue文件并引入Threejs定义初始化函数创建initMesh函数实例化ShaderMaterial类实例化TextureLoader()定义uniforms 全局变量定义vertexShader顶点着…

ad18学习笔记二:绘图工具栏、活动栏

在画原理图库的时候会经常用到顶上的绘图工具栏&#xff08;官方文档里叫做活动栏&#xff09;&#xff1a; 版本不同&#xff0c;上面的命令是不同的 ad如何自定义绘图工具栏&#xff1f; 网上介绍工具栏和设置的文章还挺多的&#xff0c;但是没有看到ad18是怎么增减绘图工具…

LeetCode高频算法刷题记录7

文章目录 1. 下一个排列【中等】1.1 题目描述1.2 解题思路1.3 代码实现 2. 两数相加【中等】2.1 题目描述2.2 解题思路2.3 代码实现 3. 括号生成【中等】3.1 题目描述3.2 解题思路3.3 代码实现 4. 滑动窗口最大值【困难】4.1 题目描述4.2 解题思路4.3 代码实现 5. 最小覆盖子串…

c++boost库学习-07-Message Queue

一、前言 boost中的消息队列&#xff08;Message Queue&#xff09;是进程间通信的一种机制&#xff0c;实际上是其内部也是采用共享内存的方式来达到进程间通信的目的。这也就意味这Message Queue有其局限性&#xff1a;只有处在同一台计算机中的不同进程才能使用消息队列进行…

在原有机械硬盘的基础上集装固态硬盘并装操作系统

1、加装固态硬盘 我的电脑出场自带的是机械硬盘&#xff08;即右边那个白色长方形&#xff0c;上面有类似于锡纸一样的东西&#xff09;&#xff0c;左边的这个光滑的正方形里面是内存条&#xff0c;可以拆开这个光滑的盖进行安装&#xff0c;而我们的固态硬盘装在左下角这个长…

性能测试——性能统计工具

性能统计工具 一、io监控命令1、io监控命令iostat2、io指标监控命令df 二、cpu监控命令1、cpu指标监控命令uptime2、cpu指标监控命令 cat /proc/cpuinfo3、cpu 指标监控命令 mpstat4、cpu指标监控命令 sar 三、mem指标监控命令1、mem指标监控命令 cat /proc/meminfo2、mem指标监…

KVM虚拟化(二)

文章目录 4.7 kvm虚拟机克隆4.7.1 完整克隆4.7.2 链接克隆 4.8 kvm虚拟机的桥接网络4.8.1 创建桥接网卡4.8.2 新虚拟机使用桥接模式4.8.3 将已有虚拟机网络修改为桥接模式 4.9 热添加技术4.9.1 kvm热添加硬盘4.9.2 kvm虚拟机在线热添加网卡4.9.3 kvm虚拟机在线热添加内存4.9.4 …

自动化测试技术解析:Appium、Sikuli与MonkeyTalk

目录 前言&#xff1a; 一、Appium自动化测试框架 它的优点是&#xff1a; 二、Sikuli自动化测试工具 它的优点是&#xff1a; 三、MonkeyTalk自动化测试工具 它的优点是&#xff1a; 四、代码样例 总结&#xff1a; 前言&#xff1a; 随着移动应用的普及&#xff0c;…

手把手教你通过PaddleHub快速实现输入中/英文本生成图像(Stable Diffusion)

近来&#xff0c;基于Diffusion的文图生成模型比较火&#xff0c;用户输入一句话&#xff0c;模型就可以生成一副对应的图像&#xff0c;还是很有意思的。本文记录了通过PaddleHub快速实现上述任务的过程&#xff0c;以供参考。 1、安装PaddlePaddle PaddleHub底层依赖于百度…

ChatGPT工作提效之生成开发需求和报价单并转为Excel格式

ChatGPT工作提效之生成开发需求和报价单并转为Excel格式 一、提出需求如何撰写百度地图标注开发的需求文档 二、针对性地连续提问推荐下一下百度地图标注文档的详细需求列表如何撰写百度地图标注开发的技术规范如何确定百度地图标注开发后的部署计划... 三、生成报价单四、运营…

【2023 · CANN训练营第一季】进阶班 应用开发深入讲解→模型推理

1 模型离线推理 各步要解析如下: Host&Device内存管理与数据传输: Host&Device上的内存申请与释放&#xff0c;内存间的相互拷贝;模型加载:将离线的om文件加载到Device上;在样例的资源初始化模块中进行。模型输入输出准备∶根据禹线om的输入输出&#xff0c;在Device…

微信小程序富文本组件mp-html

功能介绍 支持在多个主流的小程序平台和 uni-app 中使用支持丰富的标签&#xff08;包括 table、video、svg 等&#xff09;支持丰富的事件效果&#xff08;自动预览图片、链接处理等&#xff09;支持设置占位图&#xff08;加载中、出错时、预览时&#xff09;支持锚点跳转、…

【Linux】在Linux操作系统下对于权限的理解

目录 ❤️前言 正文 Linux下的不同用户 Linux的权限管理 文件访问者的分类 文件类型和访问权限 文件访问权限的修改方法 默认权限 目录权限 粘滞位 &#x1f340;结语 ❤️前言 大家好&#xff01;今天这篇文章主要是关于Linux操作系统下对于各种权限的理解问题&#…