Android(Kotlin)实现FAB图标动态切换动画(仿谷歌日历)
实现谷歌日历风格FAB图标切换动画方案
核心思路
谷歌日历的FAB图标切换是平滑旋转+图标渐变过渡的组合效果,而非生硬替换。我们需要让两个图标在切换时伴随旋转动画,同时实现淡入淡出或路径变形的过渡,而非直接调用setImageDrawable。
方案一:使用AnimatedVectorDrawable(推荐,符合Material Design规范)
这种方式利用Android的矢量动画能力,实现图标路径的平滑变形+旋转,效果最接近谷歌日历。
步骤1:准备资源
- 创建两个矢量图标:
ic_plus.xml(初始+图标)、ic_calendar.xml(切换后的日历图标) - 创建动画定义文件
fab_animator.xml,包含旋转和路径变形动画:
<set xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 旋转动画 --> <objectAnimator android:duration="300" android:propertyName="rotation" android:valueFrom="0" android:valueTo="45" android:interpolator="@android:interpolator/fast_out_slow_in"/> <!-- 图标路径变形动画(需确保两个矢量图标的pathData匹配可变形) --> <objectAnimator android:duration="300" android:propertyName="pathData" android:valueFrom="@string/plus_path" android:valueTo="@string/calendar_path" android:interpolator="@android:interpolator/fast_out_slow_in"/> </set>
- 创建
animated_vector_drawable文件fab_animated.xml,关联图标和动画:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_plus"> <target android:name="plus" android:animation="@anim/fab_animator"/> </animated-vector>
步骤2:代码实现
val fab = findViewById<FloatingActionButton>(R.id.fab) var isExpanded = false fab.setOnClickListener { val avd = fab.drawable as AnimatedVectorDrawable if (isExpanded) { avd.reverse() // 切换回+图标 } else { avd.start() // 切换到日历图标 } isExpanded = !isExpanded // 这里添加你的FAB展开/收起逻辑(比如显示子菜单) }
方案二:自定义属性动画(灵活可控)
如果不想依赖矢量图的路径变形,可通过旋转动画+淡入淡出组合实现近似效果。
代码实现
val fab = findViewById<FloatingActionButton>(R.id.fab) val plusDrawable = ContextCompat.getDrawable(this, R.drawable.ic_plus) val calendarDrawable = ContextCompat.getDrawable(this, R.drawable.ic_calendar) var isExpanded = false fab.setOnClickListener { isExpanded = !isExpanded // 1. 旋转动画 val rotateAnim = ObjectAnimator.ofFloat(fab, "rotation", if (isExpanded) 45f else 0f) rotateAnim.duration = 300 rotateAnim.interpolator = AccelerateDecelerateInterpolator() // 2. 图标淡入淡出切换 val crossFadeDrawable = TransitionDrawable(arrayOf( if (isExpanded) plusDrawable else calendarDrawable, if (isExpanded) calendarDrawable else plusDrawable )) crossFadeDrawable.isCrossFadeEnabled = true fab.setImageDrawable(crossFadeDrawable) crossFadeDrawable.startTransition(300) // 启动旋转动画 rotateAnim.start() // 这里添加你的FAB展开/收起逻辑 }
注意事项
- 使用Material Components库的FAB(
com.google.android.material.floatingactionbutton.FloatingActionButton),兼容性和动效支持更好 - 动画时长建议设置为300ms,符合Material Design的动效节奏
- 若使用方案一,需确保两个矢量图标的路径结构匹配,否则变形动画会出现异常
- 可根据需求调整动画插值器(如
FastOutSlowInInterpolator),让过渡更自然
内容的提问来源于stack exchange,提问作者oyeraghib
相关产品推荐
相关产品推荐

