In the Android daily development often use some animation knowledge. For example, we often use frame animation for ICONS in live broadcast, such as normal loading, or turntable operation, we often use tenting animation, if we want to achieve some more complex effects, we can use the property animation provided to us by the system. So how do these animations work together in our daily development? What are their characteristics and differences? Now I’m going to give you a brief introduction.

Classification of animation

View Animation

  • Action objects, views
  • Specific classification:
    • Filling between animation
    • Frame by frame animation

Here are two simple animations.

Tween Animation

Tween animation common configuration

Android :duration="3000" android:duration="3000" Android :fillBefore =" true "android:fillBefore =" true" android:fillBefore =" true "android:fillBefore =" true" Android :fillAfter = "false" android:fillAfter = "false" android:fillAfter = "false" Android :fillAfter = "false" Android :fillAfter = false Default: false Android :fillEnabled= "true" // Whether to apply the fillBefore value has no effect on the fillAfter value. The default value is true. Android :repeatMode= "restart" // Select the animation mode of repeating playback. Restart indicates positive order replay, reverse indicates reverse order replay, Android :interpolator = animation interpolator default: restart android:repeatCount = "0" // number of times to replay (so animation play number = number of times to replay +1) android:interpolator = animation interpolator This affects the speed of the animation, which will be discussed in more detail belowCopy the code

Translate animation

Android :fromXDelta="0" android:fromXDelta=" 500" android:fromXDelta=" 500 Android :fromYDelta="0" android:toYDelta="500" android:toYDelta="500" android:fromYDelta="0" android:toYDelta="500Copy the code

Scale animation

Android :fromXScale="0.0" android:fromXScale="0.0" Android :toXScale="2" android:toXScale="2 Android :toYScale=" 0" android:toYScale=" 0" Android :toYScale=" 0" Android :toYScale=" 0" Android :toYScale=" 0" Android :toYScale=" 0" android:toYScale=" 0" android:toYScale=" 0" android:toYScale=" 0" android:toYScale=" 0" android:toYScale=" 0" android:toYScale=" 0" 1.0 indicates that no scaling is normal. A value smaller than 1.0 indicates contraction. A value greater than 1.0 indicates pivotX= center point of view pivotX="50%" // X coordinate of pivot point Android :pivotY="50%" // Y coordinate of pivot point // pivotX pivotY can be a number, When the percentage, or percentage p // is set to a number (such as 50), the axis point is the origin of the View in the upper left corner plus 50px in the x and y directions. The Java code that sets this parameter is animation.absolute. // When set to % (e.g. 50%), the axis point is the origin of the upper left corner of the View in the x direction plus 50% of its width and 50% of its height in the Y direction. The corresponding argument to set this parameter in Java code is animation.relative_to_self. // When set to %p (such as 50%p), the axis point is the origin of the upper left corner of the View plus 50% of the width of the parent control in the x direction and 50% of the height of the parent control in the Y direction. The Java code that sets this parameter is animation.relative_to_parentCopy the code

Rotate

Android :duration="1000" Android :fromDegrees="0" android:fromDegrees="0" Negative = counterclockwise) Android :toDegrees="270" // The rotation Angle of the view at the end of the animation (positive = clockwise, Negative = counterclockwise) Android :pivotX="50%" // X coordinate of pivot point Android :pivotY="0" // Y coordinate of pivot point // Pivot point = center of view scaling // pivotX pivotY can be a number or percentage, Or when the percentage p // is set to a number (such as 50), the axis point is the origin in the upper left corner of the View plus a 50px point in the x and y directions. The Java code that sets this parameter is animation.absolute. // When set to % (e.g. 50%), the axis point is the origin of the upper left corner of the View in the x direction plus 50% of its width and 50% of its height in the Y direction. The corresponding argument to set this parameter in Java code is animation.relative_to_self. // When set to %p (such as 50%p), the axis point is the origin of the upper left corner of the View plus 50% of the width of the parent control in the x direction and 50% of the height of the parent control in the Y direction. The Java code that sets this parameter is animation.relative_to_parentCopy the code

Transparency Animation (Alpha)

Android :fromAlpha="1.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0" android.toalpha ="0.0 - 1 to 1)Copy the code

A combination of several animations

  • code
Private void startTranslate(){TranslateAnimation TranslateAnimation = new TranslateAnimation(0,0,0,600); translateAnimation.setFillAfter(true); translateAnimation.setDuration(1000); translateAnimation.setRepeatCount(4); translateAnimation.setRepeatMode(Animation.REVERSE); binding.first.startAnimation(translateAnimation); } private void startRotate(){ float pivotX = binding.second.getMeasuredWidth()/2; float pivotY = binding.second.getMeasuredHeight(); RotateAnimation RotateAnimation = new RotateAnimation(0,180,pivotX,pivotY); rotateAnimation.setFillAfter(true); rotateAnimation.setDuration(1000); rotateAnimation.setRepeatCount(4); rotateAnimation.setRepeatMode(Animation.REVERSE); binding.second.startAnimation(rotateAnimation); } private void startScale(){ float width = binding.third.getMeasuredWidth(); float height = binding.third.getMeasuredHeight(); ScaleAnimation = new ScaleAnimation(1,0,1,0,width/2,height/2); scaleAnimation.setFillAfter(true); scaleAnimation.setDuration(1000); scaleAnimation.setRepeatCount(4); scaleAnimation.setRepeatMode(Animation.REVERSE); binding.third.startAnimation(scaleAnimation); } private void startAlpha(){AlphaAnimation AlphaAnimation = new AlphaAnimation(1,0); alphaAnimation.setFillAfter(true); alphaAnimation.setDuration(1000); alphaAnimation.setRepeatCount(4); alphaAnimation.setRepeatMode(Animation.REVERSE); binding.four.startAnimation(alphaAnimation); }Copy the code
  • The effect

Set the view animation listener

  • public void setAnimationListener(Animation.AnimationListener listener)
  • Animation listener interface source:
public interface AnimationListener { void onAnimationStart(Animation var1); // Call void onAnimationEnd(Animation var1) when the Animation starts playing; Called when the animation has finished playing. void onAnimationRepeat(Animation var1); // called when the animation repeats. }Copy the code

Note that if we don’t want to implement all the methods we can wrap the class with an AnimatorListenerAdapter to reduce the number of useless methods we implement ourselves.

Combination of animation

Sometimes when we want a group of animations to play in sequence or at the same time, we can combine the above four animations to make a group animation. And we can control whether they play in sequence or at the same time

  • code
Private void startAnim(){// Step 1: Create a composite animation object (set to true) AnimationSet setAnimation = new AnimationSet(true); /** * Step 2: Set the properties of the combo animation * since the rotation animation set INFINITE loop (RepeatCount = INFINITE) * so the animation will not end, But infinite loop Animation of the next two lines set * so combination is invalid. * / setAnimation setRepeatMode (Animation. RESTART); setAnimation.setRepeatCount(10); Rotate = new RotateAnimation(0,360, 0) rotate = new RotateAnimation(0,360, 0) rotate = new RotateAnimation(0,360, 0) rotate = new RotateAnimation(0,360, 0) rotate = new RotateAnimation(0,360, 0) rotate = new RotateAnimation(0,360, 0) Animation. RELATIVE_TO_SELF, 0.5 f, Animation. RELATIVE_TO_SELF, 0.5 f); rotate.setDuration(3000); //rotate.setRepeatMode(Animation.RESTART); //rotate.setRepeatCount(Animation.INFINITE); 2: / / child Animation translation Animation Animation translate = new TranslateAnimation (TranslateAnimation RELATIVE_TO_PARENT, 0.5 f, TranslateAnimation. RELATIVE_TO_PARENT, 0.5 f, TranslateAnimation.RELATIVE_TO_SELF,0 ,TranslateAnimation.RELATIVE_TO_SELF,0); translate.setDuration(3000); // Animation alpha = new AlphaAnimation(1,0); alpha.setDuration(3000); //alpha.setStartOffset(3000); 4: / / child Animation zoom Animation Animation scale1 = new ScaleAnimation (,0.5 f, 1,0.5 f, Animation RELATIVE_TO_SELF, 0.5 f, Animation. RELATIVE_TO_SELF, 0.5 f); scale1.setDuration(3000); //scale1.setStartOffset(4000); / / step 4: to create a child of the animation is added to the portfolio in the animation setAnimation. AddAnimation (alpha); setAnimation.addAnimation(rotate); setAnimation.addAnimation(translate); setAnimation.addAnimation(scale1); binding.start.startAnimation(setAnimation); }Copy the code
  • perform

Frame Animation by Frame

Having briefly explained the view animations above, let’s take a look at the highlight of the day, property animations.

Attribute animation

Attribute animation is a new animation mode provided after Android 3.0.

Why use property animation?

The specific introduction of property animation

The key class role note
ViewPropertyAnimator Using the object operation to achieve attribute animation
ObjectAnimator Animation is achieved by changing values and then automatically assigning values to the properties of the object Use getXXX() and getXXX() for automatic assignment
ValueAnimator Animation is achieved by changing values and then manually assigning values to the properties of the object It is essentially a mechanism for manipulating values from start to finish according to specified rules
AnimatorSet Implement the class of composite animation

The use of property animation

The simplest ViewPropertyAnimator

  • Code:
view.animate().translationX(500);
view.animate().translationX(0);
Copy the code

Note: Animation duration is not set. Default is 300ms

The default using accelerated in the reduction of the interpolation AccelerateDecelerateInterpolator first

  • Effect:

  • introduce
  1. ViewPropertyAnimator it’s defined in the view class, the top-level superclass of all of our views. This means that almost all of our views can be simply animated using this property to achieve the desired animation effect
  2. The simplicity of the Api makes it impossible to do some more complex animations
  3. ViewPropertyAnimator supports the following animation effects:
Methods in View function Method in the corresponding ViewProPertyAnimator
setTranslationX() Set the X-axis offset translationX() translationXBy()
setTranslationY() Set the Y-axis offset translationY() translationYBy()
setTranslationZ() Set the z-axis offset translationZ() translationZBy()
setX() Set the absolute position of the X-axis x pixels x() xBy()
setY() Set the X-axis absolute position y() yBy()
setZ() Set the z-axis absolute position z() zBy()
setRotation() Set the plane to rotate by x degrees rotation() rotationBy()
setRotationX() Set the rotation along the X-axis rotationX() rotationXBy()
setRotationY() Set the rotation along the Y-axis rotationY() rotationYBy()
setScaleX() Set the landscape zoom by x scaleX() scaleXBy()
setScaleY() Set portrait zoom scaleY() scaleYBy()
setAlpha() Setting transparency alpha() alphaBy()

Use ObjectAnimator

  • Example code:
ObjectAnimator animator = ObjectAnimator. OfFloat (binding object, "rotation", 0360,0,360,0,360,0,360,0); animator.setDuration(2000); animator.start();Copy the code
  • Effect:

  • Description:
  1. Property animation Performs a set animation based on the set object and its properties
  2. Parameters of therotationIt must correspond to setRotation(float F) method in the code to be accurately identified by the system. Otherwise the animation will not execute.
  3. The ofXXX() type of ObjectAnimator corresponds to the arguments to the setXXX() method, such as the ObjectAnimator generated by the ofFloat method. The arguments to the setXXX() method must be Float.
  4. The last one in the ObjectAnimator is a variable parameter, which can be set to multiple values, and the system will animate according to the set values.
  5. Call the start method to start executing the animation
  6. ObjectAnimator supports animating any property of any object, but the property must implement getXXX() and setXXX() methods, and invalidate() must be called within the set method. To redraw the view.

Use ValueAnimator

ValueAnimator is a more flexible way to define an animation. It supports the effect of any type of change according to a specific pattern.

  • Simple code:
/** * Start valueAnimator */ private void startValueAnim(){valueAnimator Animator = ValueAnimator. OfFloat,0,360,0,360,0,360,0 (0360); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { binding.value.setRotation((float)animation.getAnimatedValue()); }}); animator.setDuration(4000); animator.start(); }Copy the code
  • The effect

  • Principle:

Use the AnimatorSet to arrange for multiple property animations to play simultaneously

In many cases, we need to play another animation depending on when one starts or ends. With the AnimatorSet, we can bundle animations into an AnimatorSet to specify whether the animations should be played at the same time, in sequence, or after a specified delay. Of course we can also nest animatorSets. To achieve more complex effects

  • The following code snippet plays the corresponding Animator object as follows:
  1. Play anim1
  2. Play anim2_1,anim2_2 and anim2_3 at the same time
  3. Play anim3
  4. Play anim4
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(anim1).before(anim2_1);
bouncer.play(anim2_1).with(anim2_2);
bouncer.play(anim2_1).with(anim2_3);
bouncer.play(anim3).after(anim2_3);
ValueAnimator anim4 = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(anim4);
animatorSet.start();
Copy the code

In addition:

  • Play two animations at once
animatorSet.playTogether(animator1, animator2);
Copy the code
  • The two animations are executed in turn
animatorSet.playSequentially(animator1, animator2);
Copy the code

PropertyValuesHolder Changes multiple properties in the same animation

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();
Copy the code

PropertyValuesHolders. OfKeyframe () split the same properties

Keyframe1 = keyFrame.offloat (0, 0); Keyframe KeyFrame2 = keyFrame.offloat (0.5f, 100); Keyframe keyFrame3 = keyFrame.offloat (1, 80); PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder); animator.start();Copy the code

Property animation listener

public static interface AnimatorListener { default void onAnimationStart(Animator animation, Boolean onAnimationStart(animation); Boolean onAnimationStart(animation); } default void onAnimationEnd(Animator animation, Boolean isReverse) {// whether the isReverse animation is played back onAnimationEnd(animation); } void onAnimationStart(Animator animation); Void onAnimationEnd(Animator animation) is called when the animation starts; Void onAnimationCancel(Animator animation) is called when the animation is finished; Void onAnimationRepeat(Animator animation) is called when the animation execution is cancelled; // call when the animation is repeated}Copy the code
  • Setting the listener
animator.addListener(new Animator.AnimatorListener(){ @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation)  { } @Override public void onAnimationRepeat(Animator animation) { } });Copy the code

Animation Interpolator

Calculates the percentage of complete for the current attribute based on the percentage of complete for time. For example, in the linear interpolator, if you animate 50% of the time and you’re 50% done,

Note: The interpolator does not return the exact value of the animation change, only the percentage of the animation change.

  1. The source code
public interface TimeInterpolator { /** * Maps a value representing the elapsed fraction of an animation to a value that  represents * the interpolated fraction. This interpolated value is then multiplied by the change in * value of an Animation to derive the Animated value at the current Elapsed animation time. * * @param input The value ranges from [0.0,1.0] and represents the percentage of elapsed animation time */ float getInterpolation(float Input); }Copy the code
  1. usage
animator.setInterpolator(new AccelerateInterpolator());
Copy the code
  1. Animation interpolator provided by the system
The name of the class introduce
AccelerateDecelerateInterpolator Speed up and slow down
AccelerateInterpolator Has been accelerated
LinearInterpolator uniform
DecelerateInterpolator Has been slow
AnticipateInterpolator First pull back and then execute the animation normally (similar to slingshot)
OvershootInterpolator After reaching the target point, it passes a few points and bounces back (similar to sitting on the sofa).
AnticipateOvershootInterpolator A combination of the two above, starting with a pull back and then executing normally and finally bouncing past the target point
BounceInterpolator To bounce at a target (similar to the effect of playing a ball)
CycleInterpolator I don’t know
PathInterpolator Custom animation completion and time completion curves. This one is more complicated. If the animation completion and time completion are drawn on an axis, then the line drawn on this axis is the execution speed of the animation
FastOutLinearInInterpolator The accelerated motion is similar to AccelerateInterpolator, except that the initial acceleration is faster. This interpolator formula uses Bessel curves, while AccelerateInterpolator formula uses exponential curves
FastOutSlowInInterpolator To accelerate in slow, similar to AccelerateDecelerateInterpolator, is only a bezier curve so it USES early fast acceleration, AccelerateDecelerateInterpolator with cosine curve
LinearOutSlowInInterpolator Decelerating motion, at a higher height than the initial velocity

Animation evaluator TypeEvaluator

The estimator calculates the value of the changed attribute based on the percentage of the current attribute changed. The interpolator determines the rule that the attribute value changes with time. And the specific change attribute value is given to the estimator to calculate.Copy the code

If you want to animate a type that is not recognized by the Android system, you can create your own evaluator by implementing the TypeEvaluator interface. The types that the Android system recognizes are int, float, or color, and are supported by the IntEvaluator, FloatEvaluator, and ArgbEvaluator type evaluators, respectively.

  1. FloateEvaluator source
Public class FloatEvaluator implements TypeEvaluator < Number > {/ * * * this method according to animation completion, and began to value the end value, value to calculate the final animation. * / public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); }}Copy the code

The fraction parameter is a float decimal that has been processed by the interpolator. It ranges from 0.0 to 1.0 and represents a percentage of the animation completed. use

animator.setEvaluator(new PointFEvaluator()); Or ValueAnimator. OfObject (new PointFEvaluator (),, obj1 obj2);Copy the code
  1. An example of custom TypeEvaluate:

Let’s say we have a custom circle whose center is the Point we define below. Now we want the circle to move in a parabolic trajectory with the code ballPoint.java

Public class BallPoint {private float pointX; private float pointY; public BallPoint(float pointX, float pointY) { this.pointX = pointX; this.pointY = pointY; } public float getPointX() { return pointX; } public float getPointY() { return pointY; }}Copy the code

BallView.java

Public class BallView extends View {private Paint circlePaint = new Paint(); Private BallPoint point = new BallPoint(0,0); public BallView(Context context) { super(context); } public BallView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public BallView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); circlePaint.setColor(Color.parseColor("#aabbcc")); circlePaint.setStyle(Paint.Style.FILL_AND_STROKE); } public BallPoint getPoint() { return point; } public void setPoint(BallPoint point) { this.point = point; invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.parseColor("#aabbcc")); canvas.drawCircle(point.getPointX(),point.getPointY(),60,circlePaint); }}Copy the code

BallViewEvaluator.java

Public class implements TypeEvaluator<BallPoint> {@override public BallPoint implements TypeEvaluator<BallPoint> {@override public BallPoint implements TypeEvaluator evaluate(float fraction, BallPoint startValue, BallPoint endValue) { float pointX = startValue.getPointX()+(fraction * endValue.getPointX() - startValue.getPointX()); float pointY = startValue.getPointY()+fraction * fraction * (endValue.getPointY()-startValue.getPointX()); BallPoint point = new BallPoint(pointX, pointY); return point; }}Copy the code

XML, and animation code

<com.zhou.animationdemo.BallView android:id="@+id/ballView" android:layout_width="match_parent" android:layout_height="match_parent"/> private void startBallAnim() { ValueAnimator animator = ValueAnimator.ofObject(new BallViewEvaluator(), new BallPoint(0, 0), new BallPoint(600, 1200)).setDuration(1000); animator.setInterpolator(new LinearInterpolator()); animator .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { BallPoint point = (BallPoint) animation.getAnimatedValue(); binding.ballView.setPoint(point); // You can also record here or do the corresponding operation}}); animator.start(); }Copy the code
  • Animation effects

Create a circular expose animation

  • Execute code when clicked:
private void showCircleAnim(){
    float finalRadius = (float) Math.hypot(binding.view.getWidth(), binding.view.getHeight());
    Animator anim = ViewAnimationUtils.createCircularReveal(binding.view, 0, 0, 0f, finalRadius);
    binding.view.setVisibility(View.VISIBLE);
    anim.setDuration(800);
    anim.start();
}
Copy the code
  • Effect:

Add animation for layout updates

  • Add code to the container layout:
<LinearLayout android:id="@+id/container"
    android:animateLayoutChanges="true"
    ...
/>
Copy the code
  • Sample code in activity (add view)
private LinearLayout.LayoutParams getLayoutParams(){
        LinearLayout.LayoutParams params =  new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,100);
        params.topMargin=30;
        return params;
    }

    int i=0;
    private void addView(){
        View v = new View(this);
        if(i%2==0){
            v.setBackgroundColor(Color.parseColor("#00FF00"));
        }else{
            v.setBackgroundColor(Color.parseColor("#0000FF"));
        }
        i++;
        v.setLayoutParams(getLayoutParams());
        binding.viewContainer.addView(v,0);
    }
Copy the code
  • The effect

Before adding animateLayoutChanges:

After adding the animateLayoutChanges configuration:

Note Reducing subviews has a similar effect and will not be shown here

Use FlingAnimation to animate the view

  • configuration
Add androidX to androidx with dependencies {implementation 'com. Android. Support: support - dynamic - animation: 28.0.0'}Copy the code
  • use
  1. Get maximum value
maxTransitionX = binding.getRoot().getWidth() - binding.fling.getWidth();
maxTransitionY = binding.getRoot().getHeight() - binding.fling.getHeight();
Copy the code
  1. Gets the current speed based on the current gesture
1. Initialization listener private GestureDetector. OnGestureListener listener = new GestureDetector. SimpleOnGestureListener () {@ Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (Math.abs(velocityX) > Math.abs(velocityY)) { showFlingAnim(velocityX); } else { showFlingAnimY(velocityY); } return true; }}; EventMotion Detector = new GestureDetector(this,listener) from onTouchListener; binding.fling.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return detector.onTouchEvent(event); }});Copy the code
  • The effect

conclusion

  1. Compare ValueAnimator class and ObjectAnimator class. In fact, both of them belong to property animation. In essence, they are the same: first change the value and then assign the value to the property of the object to achieve the animation effect.
  • The ValueAnimator class changes the value first and thenManual assignmentAttribute to an object to animate it; Is to operate indirectly on object properties
  • The ObjectAnimator class changes the value first and thenAutomatic assignmentAttribute to an object to animate it; Is to operate directly on object properties
  1. Properties to animate relationships between classes