You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 16:54:28