Why property animation? View animation is divided into tween animation and frame-by-frame animation. Tween animation can realize fading, moving, zooming and rotating in space. Frame-by-frame animation can play a group of pictures in order to achieve animation effect. So why introduce property animation?

For example, if we need to change the background of a control from red to green in less than a minute, we can’t do this with view animation, but we can do it perfectly with property animation, because property animation works by changing the properties of the control.

Another reason is the click area of the tween animation. If we move a control using the tween animation, the click event that was previously bound to the control cannot move with the control and remains where it was before.

A starting instance of ValueAnimator.

Val valueAnimator: valueAnimator = valueAnimator ofInt (0300). 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()Copy the code

We define a ValueAnimator and set a listener to it, listening for values from 0 to 300. In the listener callback function, we change the imageView coordinates to move the imageView.

ValueAnimator is responsible for the computation of the specified area. We have achieved the animation effect by listening to the calculation process and making property changes to the control.

Here are some common functions used in ValueAnimator.

1. OfInt and ofFloat functions

In the example above, we use the ofInt function, whose parameter types are variable parameter types. We can pass in any number of values. The list of values passed in represents the range of values in the animation. Such as

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()
Copy the code

The following results can be achieved

In this example, when the animation starts, the value changes from 0 to 500, then from 500 to 200, and finally from 200 to 400. So the more parameters you pass in, the more complex the animation becomes.

As an exercise, create a custom View that jumps up and down, replacing one image with each jump

Look at the renderings first

Principle analysis:

Val valueAnimator = valueAnimator. OfInt (0200, 0)Copy the code

The property animation above can make our View jump up and down, and we can make the View jump up and down by setting the number of repetitions and the repetition mode of the property animation.

valueAnimator.repeatMode = ValueAnimator.RESTART
valueAnimator.repeatCount = ValueAnimator.INFINITE
Copy the code

By listening to when the animation restarts, you can replace different images, as follows

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/project name AndroidViewBook * * * * creation time 2022/1/16 school * / class in the afternoon 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() } }Copy the code

Reference “Android custom control development and combat”