一、视图动画(View 动画)

1.1 概念

  • 视图动画(View Animation)是一种在 Android 中用于为视图对象添加动画效果的机制。它允许你通过改变视图的属性值(如位置、尺寸、透明度等)来创建平滑的动画效果。
  • 视图动画的概念是基于补间动画(Tween Animation)的,在其中定义起始和结束状态,系统会在这两个状态之间自动生成过渡动画。

1.2 使用示例

  • 首先我们要知道一点,视图动画其实就是安卓为我们提供了很多继承自 Animation 的类,例如 AlphaAnimation(透明度动画)、ScaleAnimation(缩放动画)等等。使用他们就可以实现某个视图的某个属性的动态变化。

1.2.1 透明度动画

  • 下面是一个点击按钮后将图片透明度从 100% 变成 0% 的动画:
  • 1
    2
    3
    4
    5
    6
    7
    binding.button1.setOnClickListener { 
    //透明度动画
    val animation = AlphaAnimation(1f, 0f)
    //动画持续时间
    animation.duration = 2000
    binding.imageView.startAnimation(animation)
    }
  • 参数很简单就是从哪个透明度到哪个透明度。

1.2.2 旋转动画

  • 点击按钮后将图片从指定位置作为圆心(默认为图片左上角),旋转指定角度的动画:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
binding.button2.setOnClickListener {
//旋转动画
val animation = RotateAnimation(
0f,
360f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
5f
)
//动画持续时间
animation.duration = 2000
binding.imageView.startAnimation(animation)
}
  • 参数说明:
    • 第一个参数 0f 表示起始的旋转角度为 0 度。
    • 第二个参数 360f 表示结束时的旋转角度为 360 度,即完整的一圈。
    • 第三个参数 Animation.RELATIVE_TO_SELF 表示旋转的轴相对于视图自身。
    • 第四个参数 0.5f 表示旋转的轴在 X 轴方向上的位置相对于视图自身宽度的比例为 0.5,即位于视图的中心点。
    • 第五个参数 Animation.RELATIVE_TO_SELF 表示旋转的轴相对于视图自身。
    • 第六个参数 0.5f 表示旋转的轴在 Y 轴方向上的位置相对于视图自身高度的比例为 0.5,即位于视图的中心点。
  • 你可以直接不指定后四个参数,这样旋转的中心点默认就在图片左上角。

1.2.3 平移动画

  • 点击按钮后将图片从指定位置开始向指定方向做平移操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
binding.button3.setOnClickListener {
//平移动画
val animation = TranslateAnimation(
Animation.RELATIVE_TO_SELF,
0f,
Animation.RELATIVE_TO_SELF,
1f,
Animation.RELATIVE_TO_SELF,
0f,
Animation.RELATIVE_TO_SELF,
1f
)
//动画持续时间
animation.duration = 2000
binding.imageView.startAnimation(animation)
}
  • 参数说明:
    • 第一个参数 Animation.RELATIVE_TO_SELF 表示起始点的 X 轴坐标相对于视图自身宽度的比例(即百分比),此处为 0%
    • 第二个参数 0f 表示起始点的 X 轴坐标,此处为 0
    • 第三个参数 Animation.RELATIVE_TO_SELF 表示结束点的 X 轴坐标相对于视图自身宽度的比例,此处为 100%
    • 第四个参数 1f 表示结束点的 X 轴坐标,此处为视图的宽度
    • 第五个参数 Animation.RELATIVE_TO_SELF 表示起始点的 Y 轴坐标相对于视图自身高度的比例,此处为 0%
    • 第六个参数 0f 表示起始点的 Y 轴坐标,此处为 0
    • 第七个参数 Animation.RELATIVE_TO_SELF 表示结束点的 Y 轴坐标相对于视图自身高度的比例,此处为 100%
    • 第八个参数 1f 表示结束点的 Y 轴坐标,此处为视图的高度
  • 上面的参数就实现了图片平移至图片右下角的操作。还有一个常用的4个参数的构造方法,可以直接指定平移的像素即可,感兴趣的自己探索一下。

1.2.4 缩放动画

  • 点击按钮后将以指定位置为中心点将图片进行放大或者缩小动画:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
binding.button4.setOnClickListener {
//缩放动画
val animation = ScaleAnimation(
0.5f,
1f,
0.5f,
1f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f
)
//动画持续时间
animation.duration = 2000
binding.imageView.startAnimation(animation)
}
  • 参数的作用和前面其实是差不多的,就不再介绍了。
  • 实现的效果:

1.2.5 动画集合和差值器

  • 如果我们要实现多个动画同时执行呢?比如一边旋转一边透明度发生变化?这时你需要用到一个动画集合类:AnimationSet。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
val animationSet = AnimationSet(true)

// 透明度动画
val alphaAnimation = AlphaAnimation(0f, 1f)
alphaAnimation.duration = 2000
// 将透明度变化的动画添加进动画集合
animationSet.addAnimation(alphaAnimation)

// 缩放动画
val scaleAnimation = ScaleAnimation(
1f, 1.5f, // X 轴方向的起始和结束比例
1f, 1.5f, // Y 轴方向的起始和结束比例
Animation.RELATIVE_TO_SELF, 0.5f, // 缩放中心点的 X 坐标,相对于自身宽度的比例
Animation.RELATIVE_TO_SELF, 0.5f // 缩放中心点的 Y 坐标,相对于自身高度的比例
)
scaleAnimation.duration = 2000
// 将缩放的动画添加进动画集合
animationSet.addAnimation(scaleAnimation)

//开始动画
binding.imageView.startAnimation(animationSet)
  • 在 AnimationSet 的构造函数中,传入的参数 shareInterpolator 是一个布尔值,用于指定是否共享插值器。
  • 当 shareInterpolator 设置为 true 时,表示所有添加到 AnimationSet 中的子动画都将使用相同的插值器。这意味着无论您在哪个子动画中设置插值器,都会被应用到整个 AnimationSet 中的所有子动画上。
  • 例如,假设您将 AnimationSet 设置为 true,并且在其中添加了一个缩放动画和一个旋转动画。如果您为缩放动画设置了一个加速插值器,那么旋转动画也会使用相同的加速插值器。
  • 这里顺便介绍一下几个常用的差值器:
    1. 线性差值器(LinearInterpolator):动画按照匀速进行,即在整个动画过程中,每个时间段内的变化量都相同。
    2. 减速差值器(DecelerateInterpolator):动画开始时较快,然后逐渐减速。
    3. 加速减速差值器(AccelerateDecelerateInterpolator):动画开始和结束时较慢,中间阶段加速运动。
    4. 弹跳差值器(BounceInterpolator):动画在结束时会反弹一下,形成弹跳效果。
    5. 周期差值器(CycleInterpolator):动画按照正弦或余弦曲线进行循环运动。
    6. 反向差值器(AnticipateInterpolator):动画开始前有一个向后的偏移,然后再开始正常的动画过程。
    7. 弹射差值器(OvershootInterpolator):动画在结束时会超过目标值一段距离,然后再回弹到目标值。
  • 使用也非常简单:
1
2
val scaleAnimation = ScaleAnimation(...)
scaleAnimation.interpolator = AccelerateInterpolator() // 设置加速差值器

二、属性动画(Property 动画)

2.1 概念

  • 属性动画所提供的功能和视图动画十分相似。但两者在实现原理上完全不同,而且相对视图动画来说,属性动画要强大许多。
  • 视图动画和属性动画的对比:
    1. 视图动画:
      • View 动画只能为 View 添加动画效果,且不能监听 View 相关属性的变化过程。
      • View 动画提供的动画能力较为单一,目前只支持帧动画、缩放动画、位移动画、旋转动画、透明度动画以及这些动画的集合动画。
      • View动画改变的是 View 的绘制效果,View 的真正位置和相关属性并不会改变,这也就造成了点击事件的触发区域是动画前的位置而不是动画后的位置的原因。
    2. 属性动画:
      • 属性动画作用对象不局限在 View 上,而是任何提供了 Getter 和 Setter 方法的对象的属性上。
      • 属性动画没有直接改变 View 状态的能力,而是通过动态改变 View 相关属性的方式来改变 View 的显示效果。
      • 属性动画使用更方便,可以用更简洁的代码实现相关的动画效果。
      • 属性动画上手难度较高,对于 propertyName 需要自己去挖掘,或者自己通过 Wrapper 的方式去自定义 propertyName。

2.2 使用示例

2.2.1 透明度动画

1
2
3
4
5
6
// 创建透明度动画对象
val alphaAnimation = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f)
// 设置动画属性
alphaAnimation.duration = 1000 // 动画时长,单位为毫秒
// 启动动画
alphaAnimation.start()

2.2.2 缩放动画

1
2
3
4
5
6
7
8
9
10
11
// 创建缩放动画对象
val scaleXAnimation = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f)
val scaleYAnimation = ObjectAnimator.ofFloat(view, "scaleY", 1f, 2f)

// 创建缩放动画集合
val scaleAnimationSet = AnimatorSet()
scaleAnimationSet.playTogether(scaleXAnimation, scaleYAnimation)
scaleAnimationSet.duration = 1000 // 动画时长,单位为毫秒

// 启动动画
scaleAnimationSet.start()

2.2.3 旋转动画

1
2
3
4
5
6
7
// 创建旋转动画对象
val rotationAnimation = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f)
// 设置动画属性
rotationAnimation.duration = 1000 // 动画时长,单位为毫秒
rotationAnimation.repeatCount = ValueAnimator.INFINITE // 无限循环
// 启动动画
rotationAnimation.start()

2.3 小结

  • 不用举太多例子了,因为光看上面几个例子就可以发现属性动画在代码实现上和视图动画很像,并且也可以添加差值器,使用 AnimationSet 等,可以简单的认为他就是视图动画的一个增强版,我们大部分情况下只需要指定第二个参数:propertyName,就可以实现多种多样的动画效果。
  • 此外,属性动画可以使用自定义的插值器(Interpolator)来控制动画的速度变化。你可以根据需要创建不同的插值器,实现各种动画效果。
  • 属性动画还可以通过添加监听器来监听动画开始、结束、重复等事件,并在特定事件发生时执行相应的操作。视图动画没有提供类似的事件监听机制。

三、帧动画(Frame 动画)

3.1 概念

  • 帧动画(Frame Animation),也称为逐帧动画或逐帧播放动画,是一种基于一系列预定义图像帧的动画效果。它通过按照一定的时间间隔依次显示不同的图像帧来创建动画效果。
  • 帧动画通常由多个连续的静态图像组成,这些图像按照特定的顺序排列。当播放帧动画时,系统会依次显示这些图像帧,给人以连续变化的动画感觉。

3.2 使用示例

  • 在 Android 开发中,帧动画可以使用 XML 或代码来定义。您可以在 XML 文件中指定每个帧所对应的图像资源,以及每帧显示的持续时间。然后,通过加载该 XML 文件并将其设置为一个 ImageView 的背景,即可启动帧动画。
  • 以下是一个示例 XML 文件的结构,用于定义帧动画:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item
    android:drawable="@drawable/frame1"
    android:duration="100"/>
    <item
    android:drawable="@drawable/frame2"
    android:duration="100"/>
    <!-- 其他帧 -->
    </animation-list>
  • 其中,每个 标签定义了一个图像帧。android:drawable 属性指定了该帧显示的图像资源,android:duration 属性定义了该帧显示的持续时间(以毫秒为单位)。
  • 帧动画适用于简单的、预先确定的动画效果,例如循环播放的加载动画、简单的图标动画等。然而,相对于属性动画和视图动画,它的灵活性较低,不能实现复杂的动画交互效果。
  • 同时,大多数场景下,其实帧动画可以直接用一个 GIF 图片进行替代,加上同时加载大量图片资源还可能导致 OOM,所以实际开发中使用得比较少。

四、揭露动画(Reveal 动画)

4.1 概念

  • 揭露动画(Reveal 动画)是我写博客的时候查资料看到的,就临时加进来了,之前我其实是没听过这个动画的,不过看到动画演示后我发现这种动画其实还挺常见的。
  • 揭露动画是一种视觉效果,它可以通过从屏幕的一个点开始扩散出去的方式,将一个视图或布局揭示出来,具有很好的用户体验和交互效果。

4.2 使用示例

  • 下面是一个圆形揭露动画的效果,通过 ViewAnimationUtils.createCircularReveal() 就可以创建这样一个揭露动画:
1
2
3
4
5
6
7
8
9
10
11
12
binding.imageView.setOnClickListener {
// 计算揭露动画的起始点和结束点
val cx = binding.imageView.width / 2
val cy = binding.imageView.height / 2
// hypot 函数用于计算揭露动画的最大半径
val endRadius = hypot(cx.toDouble(), cy.toDouble()).toFloat()

// 创建揭露动画对象
val anim = ViewAnimationUtils.createCircularReveal(binding.imageView, cx, cy, 0f, endRadius)
anim.duration = 1000 // 设置动画持续时间
anim.start() // 启动揭露动画
}
  • 效果演示:

五、总结

  • 以上就是安卓开发中几个比较常用的动画了,主要是用于某个控件的动态变化效果。
  • 除了上面介绍的几种动画,安卓开发中还有转场动画、矢量动画、触摸反馈动画等这里没有做介绍,感兴趣的可以自己研究研究。