Android 的三种动画

216 阅读5分钟

补间动画

补间动画的作用对象是 View,支持四种动画效果,分别是平移,缩放,旋转,透明度。它既可以是单个动画,也可以是一系列的动画组成,四种补间动画分别对应 Animation 的四个子类(TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation),它还有一个 AnimationSet 类,对应 XML 标签为 <set>,它是一个容器,可以包含若干个动画,并且内部也可以继续嵌套 <set> 集合。

比如我们有两个 View,block1 执行平移和旋转动画,block2 执行缩放和透明度动画。

translate_rotate.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="5000"
        android:fromXDelta="0"
        android:fromYDelta="3"
        android:toXDelta="0"
        android:toYDelta="-10%p" />

    <rotate
        android:duration="5000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="360" />

</set>

数值说明:3 表示以当前 View 左上角坐标加上 3px 为初始点,50% 表示以当前 View 左上角加上当前 View 宽高的 50% 做为初始点,10%p 表示以当前 View 的左上角加上父控件宽高的 10% 做为初始点,fromDegrees 是旋转开始的角度,正数代表顺时针度数,负数代表逆时针。

scale_alpha.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <scale
        android:duration="4000"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.1"
        android:toYScale="0.1" />

    <alpha
        android:duration="4000"
        android:fromAlpha="1.0"
        android:toAlpha="0.1" />

</set>

pivotX 和 pivotY 分别代表缩放起点 X 和 y 轴的坐标,fromAlpha 代表开始的透明度,数值为 0.0 到 1.0,0.0 是全透明,1.0 是不透明。

然后,分别通过各自的点击事件执行以上动画

findViewById<TextView>(R.id.block1).setOnClickListener {
    it.startAnimation(AnimationUtils.loadAnimation(this, R.anim.translate_rotate))
}
findViewById<View>(R.id.block2).setOnClickListener {
    it.startAnimation(AnimationUtils.loadAnimation(this, R.anim.scale_alpha))
}

anim.gif

我们可以看到,动画运行之后会回到原来的状态,如果想保持动画运行后的状态,可以这样设置

findViewById<TextView>(R.id.block1).setOnClickListener {
    val anim = AnimationUtils.loadAnimation(this, R.anim.translate_rotate)
    anim.fillAfter = true
    it.startAnimation(anim)
}

补间动画执行之后并未改变 View 的真实布局属性值。例如我们在 Activity 中有一个 Button 在屏幕上方,我们设置了平移动画移动到屏幕下方后保持动画最后执行状态待在屏幕下方,这时如果点击屏幕下方动画执行之后的 Button 是没有任何反应的,而点击原来屏幕上方没有 Button 的地方却能响应点击事件。

帧动画

帧动画是按照顺序播放一组预先定义好的图片,不同于补间动画,系统提供了另外一个类 AnimationDrawable 来使用帧动画。

在 drawable 文件夹下创建 xml 文件,设置动画

<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
<!--oneshot 为 true 代表只执行一次,false 代表循环执行-->
    <item android:drawable="@drawable/text_bg" android:duration="180"/>
    <item android:drawable="@drawable/text_bg2" android:duration="180"/>
    <item android:drawable="@drawable/text_bg3" android:duration="180"/>
    <item android:drawable="@drawable/text_bg4" android:duration="180"/>
</animation-list>

执行动画

 txt.setBackgroundResource(R.drawable.anim_zhen)
 txt.setOnClickListener {
     val animationDrawable = it.background as AnimationDrawable
     animationDrawable.start()
 }

属性动画

帧动画和补间动画,统称为视图动画。帧动画是通过逐帧播放图片来实现动画效果的。补间动画只有平移,缩放,旋转和透明度四种动画效果,而且只能作用于 View,也没有真正改变 View 的属性。所以,这两种动画都有一定的局限性,下面,我们一起来瞧瞧属性动画吧!

属性动画是通过在预设的规则下不断改变值,并将值设置给作用对象的属性,从而达到动画效果。这个规则可以由我们定制,其中有两个重要的工具,可以帮助我们定制值的变化规则,一个是插值器(Interpolator),一个是估值器(TypeEvaluator)。

插值器:定义了动画的变化率,能帮助我们实现非线性改变的动画

xml描述
@android:anim/accelerate_decelerate_interpolator动画始末速率较慢,中间加速
@android:anim/accelerate_interpolator动画开始速率较慢,之后慢慢加速
@android:anim/bounce_interpolator动画结束时弹起
@android:anim/decelerate_interpolator动画开始快然后慢
@android:anim/overshoot_interpolator向前弹出一定值之后回到原来位置

估值器:计算动画对应的最终属性值,系统默认提供了三种估值器:

  • IntEvaluator:整型估值器,值的类型为整型。
  • FloatEvaluator:浮点型估值器,值的类型为浮点型。
  • ArgbEvaluator:用来计算颜色值,值的类型为 ARGB 值。

属性动画主要通过两个类来使用,ValueAnimator 和 ObjectAnimator,这俩的区别在于:

  • ValueAnimator:只计算值,赋值给对象属性需要我们手动监听值的变化来进行。

  • ObjectAnimator:赋值给对象属性这一步封装进了内部,也就是自动赋值。

ValueAnimator 的使用

在 res 目录里创建 animator 目录,之后再创建 Animator resources file

text_anim.xml

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:valueFrom="30"
    android:valueTo="200"
    android:valueType="intType" />

ValueAnimator 提供了动画值的监听器,可以在 addUpdateListener 的回调中拿到属性值并手动赋值给对象属性,从而达到动画效果。

val valueAnimator =
    AnimatorInflater.loadAnimator(this, R.animator.text_anim) as ValueAnimator
valueAnimator.addUpdateListener {
    val value = it.animatedValue.toString()
    findViewById<TextView>(R.id.textView).textSize = value.toFloat()
}
valueAnimator.start()

录屏_选择区域_20221205172355.gif

ObjectAnimator 的使用

  • ofInt(int... values):值的类型是 Int,默认使用 IntEvaluator
  • ofFloat(float... values):值的类型是 Float,默认使用 FloatEvaluator
  • ofArgb(int... values):值的类型是代表颜色的 32 位的 Int,默认使用 ArgbEvaluator
val textView = findViewById<TextView>(R.id.textView)
val objectAnimator = ObjectAnimator.ofFloat(textView, "textSize", 30f, 200f)
with(objectAnimator) {
    duration = 5000
    repeatCount = 5
    repeatMode = ValueAnimator.REVERSE
    interpolator = AccelerateDecelerateInterpolator()
    start()
}

组合动画 AnimatorSet

AnimatorSet 用于把很多动画按照一定的顺序组合起来播放

val textView = findViewById<TextView>(R.id.textView)

val objectAnimator1 = ObjectAnimator.ofFloat(textView, "textSize", 30f, 200f).apply {
    repeatCount = Animation.INFINITE //无限循环
    repeatMode = ValueAnimator.REVERSE
    interpolator = BounceInterpolator()
}
val objectAnimator2 = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f).apply {
    repeatCount = Animation.INFINITE
    repeatMode = ValueAnimator.REVERSE
    interpolator = BounceInterpolator()
}

val animatorSet = AnimatorSet()
animatorSet.duration = 5000
animatorSet.play(objectAnimator1).with(objectAnimator2)
animatorSet.start()

zuhe.gif

将 objectAnimator1 和 objectAnimator2 一起播放

animatorSet.play(objectAnimator1).with(objectAnimator2)

播放 objectAnimator2 之后再播放 objectAnimator1

animatorSet.play(objectAnimator1).after(objectAnimator2)

播放 objectAnimator1 之后再播放 objectAnimator2

animatorSet.play(objectAnimator1).before(objectAnimator2)