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

不练不熟,不写就忘 之 compose 之 动画之 animateSizeAsState动画练习

场景

  • 折叠 / 展开组件(最常用场景)
  • 动态内容适配动画
  • 弹窗 / 浮层的显示 / 隐藏动画
  • 响应式布局调整
  • 交互反馈动画

折叠 / 展开组件(最常用场景)

场景描述:点击按钮或触发事件后,组件(如卡片、列表项、面板)从 “折叠状态”(小尺寸)过渡到 “展开状态”(大尺寸),或反之。
典型使用:折叠面板、展开式列表项、下拉详情页、底部弹窗展开 / 收起。

@Composable
fun CollapsibleCard() {// 状态:控制是否展开(驱动尺寸变化)var isExpanded by remember { mutableStateOf(false) }// 目标尺寸:折叠时小尺寸,展开时大尺寸(也可动态计算)val targetSize = if (isExpanded) {Size(300.dp.toPx(), 200.dp.toPx()) // 展开:宽300dp、高200dp} else {Size(300.dp.toPx(), 80.dp.toPx())  // 折叠:宽300dp、高80dp}// 动画尺寸:由 targetSize 驱动,自动过渡val animatedSize by animateSizeAsState(targetValue = targetSize,animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing),label = "collapsible_card_animation" // 无障碍标签(可选))Box(modifier = Modifier.size(width = animatedSize.width.dp,height = animatedSize.height.dp).background(Color.Blue.copy(alpha = 0.3f)).clickable { isExpanded = !isExpanded }, // 点击切换状态contentAlignment = Alignment.Center) {Text(if (isExpanded) "展开状态" else "折叠状态,点击展开")}
}

请添加图片描述

动态内容适配动画

场景描述:组件内容变化(如文本增多 / 减少、图片加载完成、列表添加 / 删除项)时,尺寸需要自适应调整,通过动画避免 “跳变”,提升视觉流畅度。
典型使用:动态文本展示、图片加载后的尺寸过渡、表单字段展开(如新增输入项)。

@Composable
fun DynamicContentCard() {var showFullText by remember { mutableStateOf(false) }// 动态文本(控制内容长度)val content = if (showFullText) {"这是一段很长的文本...(省略N字),点击可收起"} else {"这是一段简短文本,点击展开更多"}// 计算目标尺寸:根据文本长度动态适配(需结合 IntrinsicSize)val density = LocalDensity.currentval targetSize = with(density) {Size(width = 300.dp.toPx(), // 固定宽height = if (showFullText) 150.dp.toPx() else 60.dp.toPx() // 动态高)}val animatedSize by animateSizeAsState(targetValue = targetSize, label = "dynamic_content")Box(modifier = Modifier.size(animatedSize.width.dp, animatedSize.height.dp).background(Color.Green.copy(alpha = 0.3f)).clickable { showFullText = !showFullText }.padding(16.dp)) {Text(text = content)}
}

请添加图片描述

弹窗 / 浮层的显示 / 隐藏动画

场景描述:弹窗、悬浮窗、抽屉等组件,从 “隐藏状态”(极小尺寸 / 0 尺寸)过渡到 “显示状态”(正常尺寸),或反之。
优势:相比 animateEnterExit,animateSizeAsState 可更精细控制宽高各自的动画节奏(如宽先展开、高再展开)。

@Composable
fun SizeAnimatedDialog() {var isVisible by remember { mutableStateOf(false) }val density = LocalDensity.current// 目标尺寸:隐藏时0x0,显示时300x200val targetSize = if (isVisible) {with(density) { Size(300.dp.toPx(), 200.dp.toPx()) }} else {Size.Zero}val animatedSize by animateSizeAsState(targetValue = targetSize,animationSpec = spring(stiffness = Spring.StiffnessMedium), // 弹性动画label = "dialog_animation")Column(horizontalAlignment = Alignment.CenterHorizontally) {Button(onClick = { isVisible = true }) {Text("显示弹窗")}if (animatedSize.width > 0 || animatedSize.height > 0) { // 避免0尺寸占用空间Box(modifier = Modifier.size(animatedSize.width.dp, animatedSize.height.dp).background(Color.White, shape = RoundedCornerShape(8.dp)).shadow(8.dp).padding(16.dp),contentAlignment = Alignment.Center) {Column {Text("尺寸动画弹窗")Button(onClick = { isVisible = false }) {Text("关闭")}}}}}
}

请添加图片描述

响应式布局调整

场景描述:布局状态变化(如屏幕旋转、窗口大小调整、主题切换)时,组件尺寸需要平滑过渡,而非瞬间切换。
典型使用:平板 / 手机布局适配、窗口 resize 时的组件尺寸过渡、深色 / 浅色主题切换时的卡片尺寸调整。

@Composable
fun ResponsiveAnimatedBox() {// 监听屏幕方向(或窗口尺寸)val configuration = LocalConfiguration.currentval isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPEval density = LocalDensity.current// 目标尺寸:横屏时宽600dp,竖屏时宽300dp,高统一150dpval targetSize = with(density) {Size(width = if (isLandscape) 600.dp.toPx() else 300.dp.toPx(),height = 150.dp.toPx())}val animatedSize by animateSizeAsState(targetValue = targetSize,animationSpec = tween(500), // 慢节奏过渡,适配布局变化label = "responsive_animation")Box(modifier = Modifier.size(animatedSize.width.dp, animatedSize.height.dp).background(Color.Purple.copy(alpha = 0.3f)),contentAlignment = Alignment.Center) {Text(if (isLandscape) "横屏模式" else "竖屏模式")}
}

请添加图片描述

交互反馈动画

场景描述:用户交互(如点击、hover、拖拽)时,组件尺寸发生动画变化,提供视觉反馈。
典型使用:按钮点击时的 “缩放 + 尺寸变化”、卡片 hover 时的轻微放大、拖拽时的组件尺寸自适应。

@Composable
fun InteractiveAnimatedButton() {var isPressed by remember { mutableStateOf(false) }val density = LocalDensity.current// 点击时尺寸缩小10%,释放后恢复val targetSize = with(density) {if (isPressed) {Size(180.dp.toPx(), 60.dp.toPx()) // 原尺寸200x60,缩小10%} else {Size(200.dp.toPx(), 60.dp.toPx())}}val animatedSize by animateSizeAsState(targetValue = targetSize,animationSpec = spring(dampingRatio = Spring.DampingRatioHighBouncy), // 弹跳反馈label = "button_animation")Box(modifier = Modifier.size(animatedSize.width.dp, animatedSize.height.dp).background(Color.Red, shape = RoundedCornerShape(30.dp)).clickable(interactionSource = remember { MutableInteractionSource() },indication = null // 取消默认点击反馈,用尺寸动画替代) { /* 按钮逻辑 */ }.pointerInput(isPressed) {detectTapGestures(onPress = {isPressed = truetryAwaitRelease() // 等待手指抬起isPressed = false})},contentAlignment = Alignment.Center) {Text("点击有尺寸反馈", color = Color.White)}
}

请添加图片描述

关键特性与使用注意事项

    1. 核心特性
    • 状态驱动:只需修改 targetValue(Size 类型),动画自动触发,无需手动启动 / 停止。
    • 动画配置灵活:支持 tween(匀速 / 缓动)、spring(弹性)、keyframes(关键帧)等 Compose 动画规范。
    • 宽高独立动画:Size 的 width 和 height 可分别设置目标值,实现 “宽先动、高后动” 等差异化效果。
    • 无障碍支持:通过 label 参数设置动画标签,提升无障碍体验。
    1. 注意事项
    • 单位转换:animateSizeAsState 的 targetValue 接收 Size(像素单位),需通过 LocalDensity 将 dp 转换为 px(避免尺寸偏差)。
    • 避免 0 尺寸占用空间:当目标尺寸为 Size.Zero 时,可通过 if (animatedSize.width > 0) 隐藏组件,避免布局占位。
    • 性能优化:若组件频繁切换尺寸(如快速点击),建议使用 spring 动画(天然支持中断续播),或设置 finishedListener 避免动画堆积。
    • 与 Modifier.animateContentSize 区分:
      • animateSizeAsState:手动控制目标尺寸,灵活性更高(如自定义动画曲线、宽高独立控制)。
      • animateContentSize:自动适配内容尺寸变化,无需手动设置 targetSize,适合简单的 “内容驱动尺寸” 场景(如文本增多 / 减少)。

总结

animateSizeAsState 的核心价值是将 “尺寸变化” 与 “动画” 解耦,让开发者无需关注动画底层实现,只需聚焦 “目标尺寸” 的状态管理。其应用场景覆盖了 Compose 中绝大多数 “尺寸动态变化” 的需求,尤其适合需要精细控制动画节奏、宽高独立过渡的场景,是提升 UI 流畅度和用户体验的核心工具之一。

其他例子

展开式列表

package com.zqq.myapplicationimport androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateSizeAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp// 列表项数据模型
data class ExpandableItem(val id: Int,val title: String,val summary: String, // 折叠时显示的摘要val detail: String    // 展开时显示的详细内容
)// 模拟测试数据
private val testItems = listOf(ExpandableItem(id = 1,title = "Jetpack Compose 基础",summary = "Compose 是 Android 现代 UI 工具包...",detail = "Compose 基于声明式编程模型,通过状态驱动 UI,无需手动操作视图。支持重组优化、动画API、Material3 组件,可快速构建流畅、美观的 Android 界面。"),ExpandableItem(id = 2,title = "动画 API 详解",summary = "Compose 提供丰富的动画工具...",detail = "核心动画 API 包括:animateAsState(状态动画)、updateTransition(过渡动画)、Animatable(手动控制动画)、animateSizeAsState(尺寸动画)等,满足各类动效需求。"),ExpandableItem(id = 3,title = "状态管理最佳实践",summary = "Compose 状态管理的核心是...",detail = "推荐使用 remember 存储临时状态、viewModel 存储页面状态、Flow/LiveData 处理数据流。遵循“单一数据源”原则,避免状态混乱。"),ExpandableItem(id = 4,title = "LazyColumn 性能优化",summary = "LazyColumn 实现列表复用...",detail = "LazyColumn 仅渲染可见项,通过 key 参数优化重组,避免不必要的 UI 刷新。结合 rememberSaveable 可保存滚动状态,提升用户体验。")
)@Composable
fun ExpandableListItemScreen() {// 存储所有列表项的展开状态(key:item.id,value:是否展开)val expandedStates = remember { mutableStateOf<Map<Int, Boolean>>(emptyMap()) }// 切换列表项展开状态fun toggleExpanded(itemId: Int) {expandedStates.value = expandedStates.value.toMutableMap().apply {this[itemId] = !(this[itemId] ?: false)}}// 删除列表项(实际项目中可替换为真实数据删除逻辑)fun deleteItem(itemId: Int) {expandedStates.value = expandedStates.value.filterKeys { it != itemId }}LazyColumn(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),verticalArrangement = Arrangement.spacedBy(8.dp)) {items(testItems, key = { it.id }) { item ->val isExpanded = expandedStates.value[item.id] ?: falseExpandableItemView(item = item,isExpanded = isExpanded,onToggleExpanded = { toggleExpanded(item.id) },onDelete = { deleteItem(item.id) })}}
}@Composable
fun ExpandableItemView(item: ExpandableItem,isExpanded: Boolean,onToggleExpanded: () -> Unit,onDelete: () -> Unit
) {val density = LocalDensity.current// 1. 高度动画:折叠时固定高度,展开时自适应内容val targetHeight = if (isExpanded) {// 展开时:标题(48dp) + 摘要(32dp) + 详情(自适应,这里估算为120dp) + 内边距(32dp)with(density) { (48 + 120).dp.toPx() }} else {// 折叠时:标题(48dp) + 摘要(32dp) + 内边距(32dp)with(density) { (48 ).dp.toPx() }}// 动画尺寸(高度平滑过渡)val animatedSize by animateSizeAsState(targetValue = androidx.compose.ui.geometry.Size(width = with(density) { 360.dp.toPx() }, // 固定宽度(也可自适应)height = targetHeight),animationSpec = tween(durationMillis = 300), // 动画时长300mslabel = "expandable_item_height")// 2. 箭头旋转动画:展开时旋转180度val arrowRotation by animateFloatAsState(targetValue = if (isExpanded) 180f else 0f,animationSpec = tween(durationMillis = 300),label = "arrow_rotation")// 3. 列表项容器Box(modifier = Modifier.size(width = animatedSize.width.dp,height = animatedSize.height.dp).background(color = MaterialTheme.colorScheme.surfaceVariant,shape = RoundedCornerShape(12.dp)).clickable { onToggleExpanded() } // 点击整个item展开/收起.padding(16.dp)) {Column(modifier = Modifier.fillMaxWidth(),verticalArrangement = Arrangement.spacedBy(8.dp)) {// 标题行(标题 + 箭头 + 删除按钮)Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween,verticalAlignment = Alignment.CenterVertically) {Text(text = item.title,style = MaterialTheme.typography.titleMedium.copy(fontSize = 18.sp,color = MaterialTheme.colorScheme.onSurface))Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {// 删除按钮(长按显示,这里简化为始终显示)IconButton(onClick = { onDelete() },modifier = Modifier.size(24.dp)) {Icon(imageVector = Icons.Default.Delete,contentDescription = "删除",tint = Color.Red.copy(alpha = 0.7f))}// 展开/收起箭头Icon(imageVector = Icons.Default.ArrowDropDown,contentDescription = if (isExpanded) "收起" else "展开",tint = MaterialTheme.colorScheme.onSurfaceVariant,modifier = Modifier.rotate(arrowRotation))}}// 摘要(始终显示)Text(text = item.summary,style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp,color = MaterialTheme.colorScheme.onSurfaceVariant))// 详细内容(仅展开时显示)if (isExpanded) {Text(text = item.detail,style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp,color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.9f)),modifier = Modifier.padding(top = 4.dp))}}}
}// 预览函数
@Composable
fun ExpandableListItemPreview() {MaterialTheme {ExpandableListItemScreen()}
}

请添加图片描述

http://www.dtcms.com/a/585828.html

相关文章:

  • 函数模板和类模板
  • 从 0 到 1:我的 C++ 游戏开发全记录
  • 手机屏幕表面缺陷检测分割系统1:数据集说明(含下载链接)
  • 【MyBatis】 吃透 MyBatis:多表查询、SQL 注入防护(#{} vs ${})与连接池优化
  • 智能体AI的六大核心设计模式
  • 基于SLERP(Spherical Linear Interpolation) 进行旋转滤波
  • 站长工具seo查询5g5g成都市四方建设工程监理有限公司网站
  • 网站建设百科深圳网站建设公司fantodo
  • 接口自动化详细介绍
  • 深入解析多态:面向对象编程灵魂
  • 基于开源链动2+1模式AI智能名片S2B2C商城小程序的赛道力构建与品牌发展研究
  • 怎么做网站地图的样式wordpress网站后缀
  • 【报错解决】java:无效的目标发行版:17;源发行版17需要目标发行版17
  • C/C++输入输出初级(一) (算法竞赛)
  • java list<string> to string[] 怎么转换
  • 【Javaweb学习|黑马笔记|Day4】Web后端基础
  • 做智能网站系统重庆企业
  • Vue 项目实战《尚医通》,首页静态搭建 banner,笔记07
  • 构建AI智能体:八十八、大模型编辑:从一本百科全书到可修订的活页本
  • 2025.11.07 力扣每日一题
  • 网站建设 技术协议wordpress 文本框
  • pcl 构造线、平面、圆、球、圆柱体、长方体、圆锥体点云数据
  • m 的手机网站怎么做小俊哥网站建设
  • 电科金仓KingbaseES数据库全面语法解析与应用实践
  • 化妆品网站建设经济可行性分析好看的设计网站
  • 工程门户网站建设新桥做网站
  • 【开题答辩过程】以《割草机器人工作管理系统的设计与开发》为例,不会开题答辩的可以进来看看
  • 线束之插头导航器显示连接物功能文本
  • JVM(一)----- 类加载过程
  • 猎聘网网站谁做的东莞网页网站制作