Android 动画之属性动画

1,530 阅读2分钟

为什么要引入属性动画? 视图动画分为补间动画以及逐帧动画,补间动画能够实现空间的渐隐渐现、移动、缩放和旋转,逐帧动画可是顺序播放一组图片从而实现动画效果。那为什么还需要引入属性动画呢?

比如,我们需要将一个控件的背景在1分钟之内由红色变为绿色,这个效果是没有办法通过视图动画来实现的,但是可以通过属性动画来完美的实现,因为属性动画是通过改变控件的属性来实现动画效果的。

另一个原因是补间动画的点击区域问题,如果我们使用补间动画移动了一个控件,之前绑定在该控件上的点击事件无法跟随控件移动,还停留在控件之前的位置。

ValueAnimator 的入门实例。

val valueAnimator:ValueAnimator = ValueAnimator.ofInt(0,300)
valueAnimator.setDuration(5000)

valueAnimator.addUpdateListener {
    val currentValue:Int = it.animatedValue as Int
    Log.d(TAG, "start: currentValue = ${currentValue}")
    imageView.layout(currentValue,
        currentValue,
        currentValue + imageView.width,
        currentValue + imageView.height)
}
valueAnimator.start()

我们定义了一个ValueAnimator,并为其设置了一个监听器,监听从0到300的值,在监听回调函数中,改变imageView的坐标,已达到移动ImageView的效果。

valuemove.gif

由此可见,ValueAnimator负责对指定的区域进行计算,我们通过对运算过程的监听,对控件进行属性上的更改,已达到动画效果。

下面介绍在ValueAnimator中的一些常用的函数。

1、ofInt和ofFloat函数

在上面的例子中,我们使用了ofInt函数,他们的参数类型都是可变量参数类型,我们可以传入任意数量的值,传入的值列表就表示动画时值的变化范围。比如

val valueAnimator = ValueAnimator.ofFloat(0f,500f,200f,400f)
valueAnimator.setDuration(5000)
valueAnimator.addUpdateListener {
    val currentValue:Int = (it.animatedValue as Float).toInt()
    Log.d(TAG, "start: currentValue = ${currentValue}")
    imageView.layout(currentValue,
        currentValue,
        currentValue + imageView.width,
        currentValue + imageView.height)
}
valueAnimator.start()

可实现如下的效果

moveagaiin.gif

在这个例子中,动画开始时,值从0变化到500,然后从500变化到200,最后从200变化到400。所以传入的参数越多,动画就越复杂。

练习,创建一个自定义的View,View上下跳动,每一次跳动更换一张图片

先看效果图

loding.gif

原理分析:

val valueAnimator = ValueAnimator.ofInt(0,200,0)

上面的属性动画可以让我们的View上下跳动,并且可以通过设置属性动画的重复次数以及重复模式,可以让View一直上下跳动。

valueAnimator.repeatMode = ValueAnimator.RESTART
valueAnimator.repeatCount = ValueAnimator.INFINITE

通过监听动画何时重启,可以替换不同的图片,完成的代码如下

package com.example.animation

import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.appcompat.widget.AppCompatImageView

/**
 * 项目名称 AndroidViewBook
 * 创建时间 2022/1/16 6:55 下午
 */
class LoadingImageView : AppCompatImageView {
    private var mTop = 0
    private var mCurrentIndex = 0
    private val maxCount = 3

    constructor(context: Context?) : super(context!!) {
        init()
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(
        context!!, attrs
    ) {
        init()
    }

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context!!, attrs, defStyleAttr
    ) {
        init()
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        mTop = top
    }

    fun init() {
        val valueAnimator = ValueAnimator.ofInt(0, 200, 0)
        valueAnimator.repeatMode = ValueAnimator.RESTART
        valueAnimator.repeatCount = ValueAnimator.INFINITE
        valueAnimator.duration = 2000
        valueAnimator.interpolator = AccelerateDecelerateInterpolator()
        valueAnimator.addUpdateListener {
            val value = (it.animatedValue as Int)
            top = mTop - value
        }

        valueAnimator.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(animation: Animator?) {
                setImageResource(R.drawable.one)
            }

            override fun onAnimationEnd(animation: Animator?) {
                TODO("Not yet implemented")
            }

            override fun onAnimationCancel(animation: Animator?) {
                TODO("Not yet implemented")
            }

            override fun onAnimationRepeat(animation: Animator?) {
                mCurrentIndex++
                when (mCurrentIndex % maxCount) {
                    0 -> setImageResource(R.drawable.one)
                    1 -> setImageResource(R.drawable.five)
                    2 -> setImageResource(R.drawable.six)
                }
            }
        })
        valueAnimator.start()
    }
}

参考资料《Android自定义控件开发与实战》