Jetpack Compose 从入门到入门(七)

2023-05-16

本篇进入Compose 动画部分。

1.动画预览

在本系列第一篇中我们提到过,@Preview可以帮我们实现UI的预览功能,简单的交互和播放动画。

在Android Studio Bumblebee(大黄蜂)中你可以开启动画的预览,但是只支持少部分API。

在这里插入图片描述
前几天Android Studio 稳定版更新到了Chipmunk(花栗鼠),开始支持 animatedVisibility 的动画预览,这里也建议你将 Compose 升至 1.1.0 或更高版本,可以体验更完整的内容。

提示:本篇使用Compose版本为1.1.0(对应 Kotlin版本为 1.6.10)。会涉及一些实验性API,可能后面会有变动甚至取消。

简单说一先动画预览的功能。当检测到预览的ui中有支持预览的动画时,会出现一个在这里插入图片描述 图标。点击这个图标后,就可以看到具体每个动画的运动曲线,我们可以在面板上拖动、快进或放慢动画。逐帧预览过渡效果。

举个小例子,我在页面上放置一个OutlinedTextField输入框,我们看一下实际的动画预览效果。

在这里插入图片描述

是不是很强大,很便捷。好了,下面正式开始动画API部分了。

2. 高级别API

高级别api就是对基础api的封装,便于我们更好的使用。

1. AnimatedVisibility(实验性)

看名字就知道,它是一个显示隐藏的动画。

@ExperimentalAnimationApi
@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)
  • visible: 显示还是隐藏,显示执行enter,隐藏执行exit。
  • enter:显示动画,默认是从左上角开始水平和垂直方向同时展开时淡入。目前EnterTransition有四种类型:
  1. fade: fadeIn
  2. scale: scaleIn
  3. slide: slideIn, slideInHorizontally, slideInVertically
  4. expand: expandIn, expandHorizontally, expandVertically
  • exit:消失动画,默认是收缩(与显示相反)时淡出。ExitTransition如下:
  1. fade: fadeOut
  2. scale: scaleOut
  3. slide: slideOut, slideOutHorizontally, slideOutVertically
  4. shrink: shrinkOut, shrinkHorizontally,shrinkVertically

多个动画效果我们可以使用 + 运算符进行组合,使用起来很方便。具体这些动画的效果我们可以参考官方文档的示例动图:EnterTransition、ExitTransition 示例

需要注意的是,RowColumn下的Scope有对应的AnimatedVisibility拓展方法,所以默认动画略有不同。以Row举例:

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

Row中的显示隐藏动画是expandHorizontallyshrinkHorizontally,所以是水平方向从左到右展开时淡入,从右到左收缩时淡出。Column就是垂直方向过渡。这点我们在使用时需要注意。

AnimatedVisibility 还提供了传入 MutableTransitionState的变体方法。该属性有助于观察动画状态。官方demo:

// Create a MutableTransitionState<Boolean> for the AnimatedVisibility.
val state = remember {
    MutableTransitionState(false).apply {
        // Start the animation immediately.
        targetState = true
    }
}
Column {
    AnimatedVisibility(visibleState = state) {
        Text(text = "Hello, world!")
    }

    // Use the MutableTransitionState to know the current animation state
    // of the AnimatedVisibility.
    Text(
        text = when {
            state.isIdle && state.currentState -> "Visible"
            !state.isIdle && state.currentState -> "Disappearing"
            state.isIdle && !state.currentState -> "Invisible"
            else -> "Appearing"
        }
    )
    Button(
        onClick = {
            state.targetState = !state.targetState
        }
    ) {
        Text("Change")
    }
}

效果图:

在这里插入图片描述

为子项添加进入和退出动画效果

AnimatedVisibility中的内容可以使用animateEnterExit修饰符为每个子项指定不同的动画行为。我稍微修改了一下官方的demo:

val visible = remember { false }
Column(modifier = Modifier.fillMaxWidth()) {
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn(),
        exit = fadeOut()
    ) {
        Box(Modifier.fillMaxSize().background(Color.DarkGray)) {
            Box(
                Modifier
                    .align(Alignment.Center).padding(bottom = 150.dp)
                    .animateEnterExit(
                        enter = slideInVertically(),
                        exit = slideOutVertically()
                    )
                    .sizeIn(minWidth = 150.dp, minHeight = 150.dp)
                    .background(Color.Red)
            )
            Box(
                Modifier
                    .align(Alignment.Center).padding(top = 150.dp)
                    .animateEnterExit(
                        enter = slideInHorizontally(),
                        exit = slideOutHorizontally()
                    )
                    .sizeIn(minWidth = 150.dp, minHeight = 150.dp)
                    .background(Color.Blue)
            )
        }
    }
}

页面整体是灰色背景的,其中有一红一蓝两个方块居中放置。灰色背景是淡入淡出,红蓝方块是滑动效果。效果如下:

在这里插入图片描述

如果你不需要动画效果,可以设置EnterTransition.NoneExitTransition.None

添加自定义动画

通过AnimatedVisibilitytransition属性访问底层Transition实例,就可以添加自定义动画,这些动画将与AnimatedVisibility的进入和退出动画同时运行。

AnimatedVisibility(
    visible = visible,
    enter = fadeIn(),
    exit = fadeOut()
) { 
    val background by transition.animateColor { state ->
        if (state == EnterExitState.Visible) Color.Blue else Color.Red
    }
    Box(modifier = Modifier.size(128.dp).background(background))
}

效果如下:

在这里插入图片描述
关于Transition,我们后面说低级动画API时会说明。

2. AnimatedContent(实验性)

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,
    content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit
) {...}

根据源码我们可以看到默认动画效果是淡出后同时放大淡入。

  • targetState:目标状态,当它变化时会触发动画。
  • transitionSpec:过渡的动画效果。我们可以利用AnimatedContentScope中的initialStatetargetState等属性,来自定义动画效果。

下面看一个数字变换的例子:

var count by remember { mutableStateOf(0) }
Column(modifier = Modifier.fillMaxWidth().padding(10.dp)) {
    AnimatedContent(
        targetState = count,
        transitionSpec = {
            // 将当前的数字与之前的数字进行比较。
            if (targetState > initialState) {
                // 如果目标数字较大,它会向上滑动并淡入,而初始(较小)数字则向上滑动并淡入。
                slideInVertically { height -> height } + fadeIn() with
                        slideOutVertically { height -> -height } + fadeOut()
            } else {
                // 如果目标数字较小,则它会下滑并淡入,而初始数字则下滑并淡入。
                slideInVertically { height -> -height } + fadeIn() with
                        slideOutVertically { height -> height } + fadeOut()
            }.using(
                // 禁用剪切,因为要显示在边界之外。
                SizeTransform(clip = false)
            )
        }
    ) { targetCount ->
        Text(text = "$targetCount", fontSize = 33.sp)
    }
    Button(onClick = { count++ }) {
        Text("Add")
    }
}

效果如下:

在这里插入图片描述

代码中使用using传入一个SizeTransform,禁止将内容裁剪为组件大小。同时我们可以使用SizeTransform中的initialSize, targetSize属性来定义过渡中的大小变化。

3. animateContentSize

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

animateContentSizeModifier的一个扩展方法,可以在内容大小发生变化时添加动画过渡效果。此方法使用简单,这里就不过多的说明了。

4. Crossfade

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

Crossfade 在布局切换时添加淡入淡出动画。用法与AnimatedContent类似。

3. 低级别动画API

1. animateXXXAsState

animateXXXAsState 方法是 Compose中最简单的动画API,用于为单个值添加动画效果。您只需提供结束值(或目标值),该 API 就会从当前值开始向指定值播放动画。

Compose 为 Float、Color、Dp、Size、Offset、Rect、Int、IntOffset 和 IntSize 提供开箱即用的 animate*AsState 方法。通过为接受通用类型的 animateValueAsState 提供 TwoWayConverter,您可以轻松添加对其他数据类型的支持。

在这里插入图片描述

例如animateIntAsState源码如下:

@Composable
fun animateIntAsState(
    targetValue: Int,
    animationSpec: AnimationSpec<Int> = intDefaultSpring,
    finishedListener: ((Int) -> Unit)? = null
): State<Int> {
    return animateValueAsState(
        targetValue, Int.VectorConverter, animationSpec, finishedListener = finishedListener
    )
}

private val intDefaultSpring = spring(visibilityThreshold = Int.VisibilityThreshold)

默认的动画效果都是spring,它是一种弹簧(弹性)动画。

@Stable
fun <T> spring(
    dampingRatio: Float = Spring.DampingRatioNoBouncy,
    stiffness: Float = Spring.StiffnessMedium,
    visibilityThreshold: T? = null
)

不过dampingRatio默认值是1f,stiffness默认是1500f,所以实际并无明显的弹性和摆动。

2. updateTransition

animateXXXAsState适合单个属性变化的动画,如果是同时执行多个动画,可以使用updateTransition

@Composable
fun <T> updateTransition(
    targetState: T,
    label: String? = null
): Transition<T> {
    val transition = remember { Transition(targetState, label = label) }
    transition.animateTo(targetState)
    DisposableEffect(transition) {
        onDispose {
            transition.onTransitionEnd()
        }
    }
    return transition
}
  • targetState:目标状态,变化时会触发动画。
  • label:动画预览时的动画名称,用于区分动画。

下面直接照搬官方Demo,说明一下如何使用updateTransition类似。

// 定义枚举类型
enum class BoxState {
    Collapsed,
    Expanded
}

// 定义保存动画值的对象
private class TransitionData(
    color: State<Color>,
    size: State<Dp>
) {
    val color by color
    val size by size
}

页面上创建一个Box,它的大小背景色来自动画值,点击按钮更新状态。

@Composable
private fun updateTransitionDemo() {
    Column(modifier = Modifier.fillMaxWidth().padding(10.dp)) {
        var currentState by remember { mutableStateOf(BoxState.Collapsed) }
        val transitionData = updateTransitionData(currentState)
        Box(
            modifier = Modifier
                .background(transitionData.color)
                .size(transitionData.size)
        )
        Button(onClick = {
            currentState = if (currentState == BoxState.Collapsed) {
                BoxState.Expanded
            } else {
                BoxState.Collapsed
            }
        }) {
            Text(text = "Update")
        }
    }
}

这里核心是updateTransitionData方法:

@Composable
private fun updateTransitionData(boxState: BoxState): TransitionData {
    val transition = updateTransition(boxState)
    val color = transition.animateColor(label = "color") { state ->
        when (state) {
            BoxState.Collapsed -> Color.Gray
            BoxState.Expanded -> Color.Red
        }
    }
    val size = transition.animateDp(label = "dp") { state ->
        when (state) {
            BoxState.Collapsed -> 64.dp
            BoxState.Expanded -> 128.dp
        }
    }
    return remember(transition) { TransitionData(color, size) }
}

updateTransition可创建并记住Transition的实例,并更新其状态。通过transition可以使用某个animateXXX 扩展方法来定义此过渡效果中的子动画。为每个状态指定目标值。(Transition同时也有AnimatedVisibilityAnimatedContent的拓展方法。)

具体实现的效果就是展开时128dp的红色方块,收起就是64dp的灰色方块。具体效果如下:

在这里插入图片描述

3. rememberInfiniteTransition

用来创建一个无限循环的动画。

比如我们创建一个红绿色过渡的无限循环动画,代码如下:

val infiniteTransition = rememberInfiniteTransition()
val color by infiniteTransition.animateColor(
    initialValue = Color.Red,
    targetValue = Color.Green,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    )
)

Box(Modifier.fillMaxSize().background(color))

篇幅有限,我们下一篇再介绍Animatable与自定义动画部分。

参考

  • Compose官方文档 - 动画
  • 来聊聊 Jetpack Compose 动画,一篇搞定
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Jetpack Compose 从入门到入门(七) 的相关文章

  • Jetpack Compose 从入门到入门(五)

    应用中的状态是指可以随时间变化的任何值 这是一个非常宽泛的定义 xff0c 从 Room 数据库到类的变量 xff0c 全部涵盖在内 由于Compose是声明式UI xff0c 会根据状态变化来更新UI xff0c 因此状态的处理至关重要
  • Jetpack Compose 从入门到入门(六)

    本篇说说Compose中的Canvas 1 Canvas span class token annotation builtin 64 Composable span span class token keyword fun span sp
  • Jetpack Compose 从入门到入门(九)

    本篇是Compose的手势部分 点击 添加clickable修饰符就可以轻松实现元素的点击 此外它还提供无障碍功能 xff0c 并在点按时显示水波纹效果 span class token annotation builtin 64 Comp
  • Docker-compose+Dockerfile构建并启动php7.4镜像

    利用官方镜像 43 Dockerfile构建符合自己要求php7 4镜像 DockerFile apt官方源太慢时 xff0c 切换apt源该dockerfile支持的php额外扩展 bcmatch event exif gd mysqli
  • 树莓派安装 docker 和 docker-compose

    树莓派安装 docker 和 docker compose 本文地址 xff1a blog lucien ink archives 518 因为总是频繁地初始化树莓派 xff0c 所以把安装 docker 的过程也记录下来 1 安装 doc
  • Docker Compose部署Springboot+Mysql项目

    声明 xff1a 本文CSDN作者原创投稿文章 xff0c 未经许可禁止任何形式的转载 xff0c 原文链接 在上一篇文章Spring Boot Maven 43 Docker打包中 xff0c 我们实现了将Springboot项目源代码一
  • Linux安装docker-compose时使用github.com拒接连接处理

    报错如 xff1a Failed to connect to raw githubusercontent com port 443 Connection refused 主要问题就是拒接服务到github com xff0c 你可以测试pi
  • Docker Compose

    1 Docker Compose官网说明 理解 Compose是Docker官方的开源项目 需要安装 xff01 Dockerfile 让程序在任何地方运行 web服务 redis mysql nginx 多个容器 run Compose的
  • 【Docker】ubuntu下安装Docker和Docker-Compose

    文章目录 一 先决条件 xff08 一 xff09 操作系统要求 xff08 二 xff09 卸载旧版本Docker 二 安装Docker xff08 一 xff09 使用存储库安装Docker 三 安装Docker Compose xff
  • Jetpack刷机TX2(大坑)【记录问题】

    Jetpack刷机TX2 xff08 大坑 xff09 1 Jetson TX2 刷机时遇到的坑 xff1a https blog csdn net zshluckydogs article details 79855631 xff01 x
  • Jetson查看JetPack版本(tool)

    Jetson查看JetPack版本 查看L4T版本 我的L4T版本为 32 5 1 在官网查找对应的jetpack版本 This page includes access to previously released versions of
  • docker、docker-compose和Portainer的安装

    一 docker安装 span class token comment 安装docker相关依赖 span yum span class token function install span y yum utils device mapp
  • Docker(四)----Docker-Compose 详解

    1 什么是Docker Compose Compose项目来源于之前的fig项目 xff0c 使用python语言编写 与docker swarm配合度很高 Compose 是 Docker 容器进行编排的工具 xff0c 定义和运行多容器
  • 【ROS2 入门】Jeston TX1 JetPack_4.6.3环境 ubuntu 18.04 ROS2 安装

    大家好 xff0c 我是虎哥 xff0c 从今天开始 xff0c 我将花一段时间 xff0c 开始将自己从ROS1切换到ROS2 xff0c 在上一篇中 xff0c 我们再虚拟机环境中安装了 ROS2 eloquent版本 xff0c 并完
  • Android DataBinding的基本使用

    5 DataBinding https developer android com topic libraries data binding custom conversions 数据绑定库是一种支持库 借助该库 您可以使用声明性格式 而非
  • Jetpack学习之Lifecycle

    Jetpack是Google为了解决Android架构问题而引入的 Google官方说的说法 Jetpack是一套库 工具和指南 可以帮助开发者更轻松地编写应用程序 Jetpack中的组件可以帮助开发者遵循最佳做法 摆脱编写样板代码的工作并
  • Jetpack-Compose 学习笔记(五)—— State 状态是个啥?又是新概念?

    系列第五篇 进入 Compose 中有关 State 状态的学习 前面几篇笔记讲了那么多内容 都是基于静态界面的展示来说的 即给我一个不变的数据 然后将它展示出来 如何在 Compose 中构建一个随数据而变化的动态界面呢 相信看完这篇就知
  • Nvidia TX2 刷机教程 JetPack-L4T-3.0-linux-x64.run

    前言 本教程特别针对刷机被墙的朋友 如果没有被墙 其实按照官方文档一步一步操作就行 这期间我参考了特别多的网页 也去nvidia官方论坛问过 其实截止到2019年2月16日 jetpack3 3 是可以很轻松的被装上的 主要是3 0被墙 而
  • jetpack compose 屏幕适配

    fun Int sdp Dp val screenDp Resources getSystem displayMetrics widthPixels Resources getSystem displayMetrics density re
  • Jetpack学习之LiveData

    LiveData是一个可被观察的数据容器类 它是一个数据的容器 将数据包装起来 使数据成为被观察者 当该数据发生变化时 观察者能够获得通知 和ViewModel的关系 ViewModel用于存放页面所需的各种数据 以及数据相关的业务逻辑 因

随机推荐

  • Vue3使用axios请求数据后使用v-for显示不出数据

    getAccount let that 61 this axios url 39 http localhost 8080 api GetAllUniAccounts 39 method 39 post 39 then res 61 gt t
  • Axios请求中Content-Type的使用总结

    Axios请求头中的Content Type常见的有3种 xff1a 1 Content Type application json 2 Content Type application x www form urlencoded 3 Co
  • mycat全局序列号数据库方式

    1 server xml lt property name 61 34 sequnceHandlerType 34 gt 1 lt property gt 2 schema xml lt table name 61 34 z test 34
  • Warning: Packets out of order. Expected 8 received 0

    环境 xff1a linux 43 mycat 43 hyperf 43 es 原因 xff1a mycat进行了分片 解决 xff1a 修改文件路径 xff1a vendor hyperf database src Connectors
  • Ubuntu下不能切换中文,qt creator无法输入中文,sogo输入法(详细步骤)

    目录 xff1a 1 解决ubuntu 不支持切换中文 xff0c 并安装sogo输入法步骤 xff1b 2 解决Qt Creator不支持中文输入 xff1a 详细步骤 xff1a 一 解决ubuntu 不支持切换中文 xff0c 并安装
  • wordpress插件API入口

    lt php 64 package Moodo Zhong 64 version 1 0 0 Plugin Name Moodo Zhong Plugin URI http wordpress org plugins hello dolly
  • PHP源码目录说明

    1 build 和编译有关的目录 2 ext 扩展库代码 xff0c 例如 MySQL zlib iconv 等我们熟悉的扩展库 其中 ext standard 目录下是常用的标准函数集 3 main 主目录包含主要的 PHP 宏和定义 4
  • 终端程序定义示例

    interrupt 0 外部中断0 interrupt 1 T0中断 interrupt 2 外部中断1 interrupt 3 T1中断 interrupt 4 串口中断 我来告诉你实质 xff1a 单片机的中断处理是这样工作的 xff0
  • lisp学习资料

    中文LISP学习网站 CL HTTP franz com 217条消息 使用hunchentoot搭建Lisp web 服务器 keyboardOTA的博客 CSDN博客
  • GO调用C语言之字符串传递

    C xff1a hello h include lt stdlib h gt include lt stdio h gt include lt string h gt char abc int strnum char str C hello
  • C++-基础语法-cin.getline() 与 cin.get() 的区别,以及getline()函数使用方法

    参考声明 xff1a C 43 43 primer plus https blog csdn net best fiends zxh article details 53064771 https blog csdn net u0114216
  • Kotlin开发中的一些Tips(二)

    接着上一篇 xff0c 最近又整理了一些 1 作用域函数选择 目前有let run with apply 和 also五个作用域函数 官方文档有张表来说明它们之间的区别 xff1a 总结一下有几点区别 xff1a apply和also返回上
  • Jetpack Compose 从入门到入门(一)

    Jetpack Compose 是用于构建原生 Android 界面的新工具包 它使用更少的代码 强大的工具和直观的 Kotlin API xff0c 可以帮助您简化并加快 Android 界面开发 xff0c 打造生动而精彩的应用 它可让
  • Jetpack Compose 从入门到入门(二)

    开始布局部分 这部分我个人感觉没有必要每个组件 属性都详细说到 xff0c 否则篇幅会很长 建立起Compose中的组件与 Android Views的一个对应关系就够了 具体还是需要在实际的使用中去熟悉 1 Column 子元素按竖直顺序
  • Jetpack Compose 从入门到入门(三)

    本篇开始介绍Jetpack Compose 中的修饰符Modifier 修饰符可以用来执行以下操作 xff1a 更改可组合项的大小 布局 行为和外观 添加信息 xff0c 如无障碍标签 处理用户输入 添加高级互动 xff0c 如使元素可点击
  • 【操作系统】Linux系统中直接优化提升CPU性能(已解决)

    文章目录 问题 xff1a 服务器CPU没有调用最高性能 xff0c 导致跑算法的时候处理速度慢一 BIOS方法二 终端直接设置CPU调节器方法1 查看当前CPU调节器2 安装各种依赖库3 最后安装cpufrequtis工具包并设置CPU调
  • Jetpack Compose 从入门到入门(四)

    本篇开始介绍Jetpack Compose 中常用的组件 有一部分之前的文章中也出现过 xff0c 今天详细说明一下 1 Text 日常最常用的应该就是显示文字 xff0c 所以有必要说一下Text控件 首先源码如下 xff1a span
  • Jetpack Compose 从入门到入门(五)

    应用中的状态是指可以随时间变化的任何值 这是一个非常宽泛的定义 xff0c 从 Room 数据库到类的变量 xff0c 全部涵盖在内 由于Compose是声明式UI xff0c 会根据状态变化来更新UI xff0c 因此状态的处理至关重要
  • Jetpack Compose 从入门到入门(六)

    本篇说说Compose中的Canvas 1 Canvas span class token annotation builtin 64 Composable span span class token keyword fun span sp
  • Jetpack Compose 从入门到入门(七)

    本篇进入Compose 动画部分 1 动画预览 在本系列第一篇中我们提到过 xff0c 64 Preview可以帮我们实现UI的预览功能 xff0c 简单的交互和播放动画 在Android Studio Bumblebee xff08 大黄