当前位置: 首页 > news >正文

Jetpack Compose 动画正式开始学习

1. 简单值动画   

   //将一个Color简单值 从一个值 变化到另一个 另一个简单值 就用 animateColorAsStateval backgroundColor by animateColorAsState(if (tabPage == TabPage.Home) Purple100 else Green300)

 动画其实就是 一个状态不停发生改变导致 组件不断重组产生的过程 

2. 可见性动画

2.1 按钮展开收缩

LazyColumn

val lazyListState = rememberLazyListState()
/*** Returns whether the lazy list is currently scrolling up.* 返回LazyColumn是否 当前向上滚动*/
@Composable
private fun LazyListState.isScrollingUp(): Boolean {var previousIndex by remember(this) { mutableStateOf(firstVisibleItemIndex) }var previousScrollOffset by remember(this) { mutableStateOf(firstVisibleItemScrollOffset) }return remember(this) {derivedStateOf {if (previousIndex != firstVisibleItemIndex) {previousIndex > firstVisibleItemIndex} else {previousScrollOffset >= firstVisibleItemScrollOffset}.also {previousIndex = firstVisibleItemIndexpreviousScrollOffset = firstVisibleItemScrollOffset}}}.value
}
     // Toggle the visibility of the content with animation.AnimatedVisibility(visible = extended) {Text(text = stringResource(R.string.edit),modifier = Modifier.padding(start = 8.dp, top = 3.dp))}

可见性动画  AnimatedVisibility

2.2 消息从顶部滑入和滑出

 

 

@Composable
private fun EditMessage(shown: Boolean) {Log.d("ning","".plus(shown))AnimatedVisibility(visible = shown,//垂直出来enter = slideInVertically(// 通过从 initialOffsetY 向下滑动到 0 来进入initialOffsetY = { fullHeight -> fullHeight },//这里要传入一个带参数的函数,返回的是你需要告诉动画系统控件初始位置或结束位置,参数是动画系统告诉你的控件的高度//LinearOutSlowInEasing:传入元素使用减速缓和设置动画,减速缓和以峰值速度(元素移动的最快点)开始过渡 , 慢慢减速 ,直到停止animationSpec = tween(durationMillis = 150, easing = LinearOutSlowInEasing)//帧动画),//垂直回去exit = slideOutVertically(// 通过从 initialOffsetY 向上滑动到 targetOffsetY 来退出targetOffsetY = { fullHeight -> -fullHeight },//FastOutLinearInEasing :退出屏幕的元素使用加速度缓和,从静止开始,以峰值速度结束animationSpec = tween(durationMillis = 150, easing = FastOutLinearInEasing))) {Surface(modifier = Modifier.fillMaxWidth(),color = MaterialTheme.colorScheme.secondary,shadowElevation = 4.dp) {Text(text = stringResource(R.string.edit_message),modifier = Modifier.padding(16.dp))}}
}

3.内容大小动画

      Column(modifier = Modifier.fillMaxWidth().padding(16.dp)// This `Column` animates its size when its content changes.//加了,当内容发生变化的时候,会慢慢的撑开.animateContentSize()) {Row {Icon(imageVector = Icons.Default.Info,contentDescription = null)Spacer(modifier = Modifier.width(16.dp))Text(text = topic,style = MaterialTheme.typography.bodyLarge)}if (expanded) {Spacer(modifier = Modifier.height(8.dp))Text(text = stringResource(R.string.lorem_ipsum),textAlign = TextAlign.Justify)}}

4.多值动画

 

@Composable
private fun HomeTabBar(backgroundColor: Color,tabPage: TabPage,onTabSelected: (tabPage: TabPage) -> Unit
) {TabRow(selectedTabIndex = tabPage.ordinal,containerColor = backgroundColor,indicator = { tabPositions ->HomeTabIndicator(tabPositions, tabPage)}) {HomeTab(icon = Icons.Default.Home,title = stringResource(R.string.home),onClick = { onTabSelected(TabPage.Home) })HomeTab(icon = Icons.Default.AccountBox,title = stringResource(R.string.work),onClick = { onTabSelected(TabPage.Work) })}
}

 

@Composable
private fun HomeTabIndicator(//TabPosition 当前选项卡的位置信息//对应tabrow里面的组件集合 有顺序索引的tabPositions: List<TabPosition>,tabPage: TabPage
) {// 表示当前选择的选项卡的指示器。// 默认情况下,这将是一个 TabRowDefaults.Indicator,// 使用 TabRowDefaults.tabIndicatorOffset 修饰符对其位置进行动画处理。// 请注意,此指示器将强制填满整个选项卡,// 因此您应该使用 TabRowDefaults.tabIndicatorOffset 或类似工具在此空间内对实际绘制的指示器进行动画处理,并从头开始提供偏移量。//自定义选项卡指示器//多值动画 :在状态发生改变时,有多个动画值要一起发生改变//设置一个Transition  并使用 targetState 提供的目标 对其进行更新.//当 targetState 更改时,Transition 将朝着为 新 targetState 指定的目标值 运行其所有子动画//可以使用 Transition 动态添加子动画 :Transition.animateFloat 、animateColor、 animateValue 等。val transition = updateTransition(tabPage,//目标状态label = "Tab indicator")//当前选项卡的位置信息的左侧  用动画的方式描绘当前选项卡位置信息的左侧val indicatorLeft by transition.animateDp(//实现弹性效果transitionSpec = {//指标向右移动,左边缘比右边缘移动得慢if (TabPage.Home isTransitioningTo TabPage.Work) {spring(//刚度// 右边比左边快stiffness = Spring.StiffnessVeryLow)} else {spring(//刚度stiffness = Spring.StiffnessMedium)}},label = "Indicator left") { tabPage ->//参数就是那个枚举状态tabPositions[tabPage.ordinal].left}//当前选项卡的位置信息的右侧val indicatorRight by transition.animateDp(//实现弹性效果transitionSpec = {//指标向右移动,左边缘比右边缘移动得慢if (TabPage.Home isTransitioningTo TabPage.Work) {spring(//刚度// 右边比左边快stiffness = Spring.StiffnessMedium)} else {spring(//刚度stiffness = Spring.StiffnessVeryLow)}},label = "Indicator right") { tabPage ->//参数就是那个枚举状态tabPositions[tabPage.ordinal].right}val color by transition.animateColor(label = "Border color") { page ->if (page == TabPage.Home) Purple700 else Green800}Box(Modifier//这个修饰符用于包裹 Box 的内容,使其尺寸与内容尺寸相匹配,并将内容对齐到底部的起始位置。.wrapContentSize(align = Alignment.BottomStart).offset(x = indicatorLeft)//偏移量.width(indicatorRight - indicatorLeft).padding(4.dp)//这个一定要放下面,要不然撑不开.fillMaxSize().border(BorderStroke(2.dp, color),RoundedCornerShape(4.dp)))
}

5.重复动画

@Composable
private fun LoadingRow() {// Creates an `InfiniteTransition` that runs infinite child animation values.//无限val infiniteTransition = rememberInfiniteTransition()val alpha by infiniteTransition.animateFloat(initialValue = 0f,targetValue = 1f,// `infiniteRepeatable` repeats the specified duration-based `AnimationSpec` infinitely.animationSpec = infiniteRepeatable(// The `keyframes` animates the value by specifying multiple timestamps.animation = keyframes {//关键字// One iteration is 1000 milliseconds.durationMillis = 1000 //一次动画的时间  就是从0-1的时间// 0.7f at the middle of an iteration.//500毫秒的时候 alpha的值是1f1f at 1000 //外面延迟 协程3秒 所以 执行了三次},// When the value finishes animating from 0f to 1f, it repeats by reversing the// animation direction.repeatMode = RepeatMode.Reverse // 0 - 1 -> 0 ->1), label = "")Row(modifier = Modifier.heightIn(min = 64.dp).padding(16.dp),verticalAlignment = Alignment.CenterVertically) {Box(modifier = Modifier.size(48.dp).clip(CircleShape).background(Color.LightGray.copy(alpha = alpha)))Spacer(modifier = Modifier.width(16.dp))Box(modifier = Modifier.fillMaxWidth().height(32.dp).background(Color.LightGray.copy(alpha = alpha)))}
}

 6.手势动画

/*** The modified element can be horizontally swiped away.* 修改后的元素可以水平滑动* 当元素轻扫到屏幕边缘时调用。* @param onDismissed Called when the element is swiped to the edge of the screen.*/
private fun Modifier.swipeToDismiss(onDismissed: () -> Unit//composed 合成的意思
): Modifier = composed {val offsetX = remember {Animatable(0f)}pointerInput(Unit) {//衰减动画通常在投掷姿势后使用,用于计算投掷动画最后的固定位置  样条衰减  指数衰减 两个衰减器  这个是样条衰减val decay = splineBasedDecay<Float>(this)coroutineScope {//可以做一些手势相关的操作//创建一个修饰符,用于处理修改元素区域内的光标输入//pointerInputs 可以调用 PointerInputScope.awaitPointerEventScope,//以安装可以等待PointerEventScope 的光标输入处理程序while (true) {//等待触摸按下事件//awaitPointerEventScope :挂起并安装指针输入块,该块可以等待输入事件并立即响应他们//awaitFirstDown: 读取事件,直到收到第一个 downval pointerId = awaitPointerEventScope {awaitFirstDown().id}val velocityTracker = VelocityTracker()//专门计算速度的类//等待拖动事件awaitPointerEventScope {//监听水平滑动horizontalDrag(pointerId) { change ->val horizontalDragOffset = offsetX.value + change.positionChange().xlaunch {offsetX.snapTo(horizontalDragOffset)//平滑过渡的效果}//记录滑动的位置velocityTracker.addPosition(change.uptimeMillis,change.position)//偏移量 与 所用时间//消费掉手势事件,而不是传递给外部change.consume()}}// 拖动完成,计算投掷的速度val velocity = velocityTracker.calculateVelocity().x//我们需要计算投掷的最终位置,以决定是将元素划回原始位置,还是将其划开并调用回调val targetOffsetX = decay.calculateTargetValue(offsetX.value, velocity)offsetX.updateBounds(lowerBound = -size.width.toFloat(),upperBound = size.width.toFloat())launch {if (targetOffsetX.absoluteValue <= size.width) {//元素范围之内  划回来offsetX.animateTo(targetValue = 0f, initialVelocity = velocity) //以原来的速度划回来} else {//启动衰减动画offsetX.animateDecay(velocity,decay)onDismissed()}}}}}.offset {IntOffset(offsetX.value.roundToInt(), 0)}}

 

/*** Shows a row for one task.** @param task The task description.* @param onRemove Called when the task is swiped away and removed.*/
@Composable
private fun TaskRow(task: String, onRemove: () -> Unit) {Surface(modifier = Modifier.fillMaxWidth().swipeToDismiss(onRemove),shadowElevation = 2.dp) {Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {Icon(imageVector = Icons.Default.Check,contentDescription = null)Spacer(modifier = Modifier.width(16.dp))Text(text = task,style = MaterialTheme.typography.bodyLarge)}}
}

相关文章:

Jetpack Compose 动画正式开始学习

1. 简单值动画 //将一个Color简单值 从一个值 变化到另一个 另一个简单值 就用 animateColorAsStateval backgroundColor by animateColorAsState(if (tabPage TabPage.Home) Purple100 else Green300) 动画其实就是 一个状态不停发生改变导致 组件不断重组产生的过程 2.…...

iOS 17.4报错: libopencore-amrnb.a[arm64]

iOS 17.4报错&#xff1a; libopencore-amrnb.a[arm64] iOS 17.4 模拟器运行报错解决方案 iOS 17.4 模拟器运行报错 Building for ‘iOS-simulator’, but linking in object file (/XXX/lib/libopencore-amrnb.a[arm64]2) built for ‘iOS’ 解决方案 在Podfile里添加如下设…...

鼓楼夜市管理wpf+sqlserver

鼓楼夜市管理系统wpfsqlserver 下载地址:鼓楼夜市管理系统wpfsqlserver 说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于C#wpf架构和sql server数据库 功能模块&#xff1a; 登录注册 鼓楼夜市管理系统主界面所有店铺信…...

【五、接口自动化测试】5分钟掌握python + requests接口测试

你好啊&#xff01;我是山茶&#xff0c;一个持续探索AI 测试的程序员&#xff01; 在做接口测试时&#xff0c;在python中内置了HTTP库 urllib&#xff0c;可以用于发送http请求。基于urllib二次封装的三方库Requests&#xff0c;相较于urllib更佳简介易用。所以&#xff0c;…...

双边市场的基本理论

双边市场由两个不同类型的用户组成&#xff0c;通过一个中介机构或平台进行交易&#xff0c;其中一边用户的决策会影响另一边用户的结果。这种影响被称为间接网络外部性&#xff0c;它导致了平台在吸引和平衡两边用户时面临的挑战。 平台定价在双边市场中成为核心问题&#xf…...

R统计学2 - 数据分析入门问题21-40

往期R统计学文章&#xff1a; R统计学1 - 基础操作入门问题1-20 21. 如何对矩阵按行 (列) 作计算&#xff1f; 使用函数 apply() vec 1:20 # 转换为矩阵 mat matrix (vec , ncol4) # [,1] [,2] [,3] [,4] # [1,] 1 6 11 16 # [2,] 2 7 12 17 # [3,] …...

蓝桥杯2023年-买瓜(dfs,类型转换同样耗时)

题目描述 小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜&#xff0c;每个瓜的重量为 Ai 。 小蓝刀功了得&#xff0c;他可以把任何瓜劈成完全等重的两份&#xff0c;不过每个瓜只能劈一刀。 小蓝希望买到的瓜的重量的和恰好为 m 。 请问小蓝至少要劈多少个瓜才能买到重量恰好…...

生成式人工智能服务安全基本要求实务解析

本文尝试明晰《基本要求》的出台背景与实践定位&#xff0c;梳理《基本要求》所涉的各类安全要求&#xff0c;以便为相关企业遵循执行《基本要求》提供抓手。 引言 自2022年初以来&#xff0c;我国陆续发布算法推荐、深度合成与生成式人工智能服务相关的规范文件&#xff0c;…...

nginx详解,配置http,https,负载均衡,反向代理,SMTP 代理步骤说明

Nginx 是一款高性能的开源 Web 服务器,同时也可以用作反向代理服务器、负载均衡器、HTTP 缓存、HTTPS 中继、以及作为邮件代理服务器等。以下是 Nginx 可以实现的一些常见用途: 静态内容服务: Nginx 可以用来提供静态内容,比如 HTML、CSS、JavaScript 文件等。 动态内容服务…...

ARTS Week 20

Algorithm 本周的算法题为 1222. 可以攻击国王的皇后 在一个 下标从 0 开始 的 8 x 8 棋盘上&#xff0c;可能有多个黑皇后和一个白国王。 给你一个二维整数数组 queens&#xff0c;其中 queens[i] [xQueeni, yQueeni] 表示第 i 个黑皇后在棋盘上的位置。还给你一个长度为 2 的…...

python如何读取文件

这里的文件是txt文件&#xff0c;office文件不支持。 假如有一个pi_digits的txt文件&#xff0c;里面的内容是“3.1415926” 如果要读取这个文件的内容&#xff0c;需要调取pathlib模块&#xff0c;并把路径告知python。同时python文件必须要和目标读取文件在一个文件夹里。 …...

InnoDB和MyISAM存储引擎

InnoDB mysql默认存储引擎 支持事务&#xff0c;行级锁&#xff08;并发量大&#xff09;&#xff0c;外键约束&#xff0c;容量大&#xff0c;支持缓存&#xff0c;支撑主键自增&#xff0c; 全文检索&#xff0c;不存储表的总行数&#xff0c;需要sql逐行统计 MyISAM 不…...

DataGrip 2023:让数据库开发变得更简单、更高效 mac/win

JetBrains DataGrip 2023是一款功能强大的数据库IDE&#xff0c;专为数据库开发和管理而设计。通过DataGrip&#xff0c;您可以连接到各种关系型数据库管理系统(RDBMS)&#xff0c;并使用其提供的一组工具来查询、管理、编辑和开发数据库。 DataGrip 2023软件获取 DataGrip 2…...

突破编程_C++_设计模式(命令模式)

1 命令模式的基本概念 C 命令模式是一种设计模式&#xff0c;它允许将请求封装为一个对象&#xff0c;从而可以用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。命令模式的主要目的是将请求封装为对象&#xff0c;从而可…...

LeetCode102题:二叉树的层序遍历(python3)

代码思路&#xff1a;使用队列先进先出的特性&#xff0c;queue[]不为空进入for循环&#xff0c;tmp存储每层的节点&#xff0c;将结果添加至res[]中。 python中使用collections中的双端队列deque()&#xff0c;其popleft()方法可达到O(1)时间复杂度。 class Solution:def lev…...

linux服务器保存git账号密码命令

1.保存git账号密码 git config --global credential.helper store 2.查看git账号密码 cd回车 ls -a cat .git-credentials 步骤: 先输入 git config --global credential.helper store 然后进入代码目录&#xff0c;git pull 会提示输入git账号、密码&#xff0c;因为我们提前输…...

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的田间杂草检测系统(深度学习模型+UI界面+Python代码+训练数据集)

摘要&#xff1a;开发用于田间杂草识别的系统对提高农业运营效率和提升作物产出至关重要。本篇文章详尽阐述了如何应用深度学习技术开发一个用于田间杂草识别的系统&#xff0c;并附上了完备的代码实现。该系统基于先进的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5…...

java Lambda表达式如何支持静态方法引用

java Lambda表达式如何支持静态方法引用 在Java中&#xff0c;Lambda表达式支持静态方法引用&#xff0c;允许你直接使用静态方法作为Lambda表达式的实现。静态方法引用使用类名和方法名来引用静态方法。 下面是一个简单的示例&#xff0c;展示了如何在Lambda表达式中使用静态…...

SpringMVC04、Controller 及 RestFul

4、Controller 及 RestFul 4.1、控制器Controller 控制器复杂提供访问应用程序的行为&#xff0c;通常通过接口定义或注解定义两种方法实现。控制器负责解析用户的请求并将其转换为一个模型。在Spring MVC中一个控制器类可以包含多个方法在Spring MVC中&#xff0c;对于Contr…...

【机器学习300问】33、决策树是如何进行特征选择的?

还记得我在【机器学习300问】的第28问里谈到的&#xff0c;看决策树的定义不就是if-else语句吗怎么被称为机器学习模型&#xff1f;其中最重要的两点就是决策树算法要能够自己回答下面两问题&#xff1a; 该选哪些特征 特征选择该选哪个阈值 阈值确定 今天这篇文章承接上文&…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

《Docker》架构

文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器&#xff0c;docker&#xff0c;镜像&#xff0c;k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...