A reasonable use of animation in App can achieve friendly and pleasant user experience. Animation in Android includes View animation, property animation, frame animation, layout animation, transition animation, etc. After 5.x, vector animation has been added. These animations are widely used in daily development, so it is necessary to make a complete summary.


First, View animation

The View animation defines four basic animations: gradient Alpha, Rotate, zoom, and Translate. A variety of interactive effects can be achieved through the combination of these four basic animations. View animation is very simple to use, not only through XML files to define the animation, but also through Java code to achieve the animation process.

1.Xml file defines View animation

Defining a View animation through XML involves some public attributes (not hinted at on AndroidStudio) :

android:Duration Animation durationandroid:FillAfter istrueWhen the animation ends, the View remains as it was when the animation endedandroid:FillBefore fortrueAt the end of the animation, the View will be restored to its original stateandroid:RepeatCount Number of times the animation is repeatedandroid:RepeatMode animation repeatMode, restart starts again when playback, reverse plays back when playback, this property needs andandroid:RepeatCount is used togetherandroid:Interpolator, equivalent to a gearbox, changes the execution speed of different stages of animationCopy the code

These properties are inherited from the Animation and can be used directly in the alpha, Rotate, Scale, and Translate tags. Defining View animations using XML files requires the creation of an Anim folder in the project’s RES directory, where all XML-defined View animations are placed. The gradient view_anim_alpha. XML:

<?xml version="1.0" encoding="utf-8"? >
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="2000"
       android:fromAlpha="1.0"
       android:toAlpha="0">
</alpha>Copy the code

Rotating view_anim_rotate. XML:

<?xml version="1.0" encoding="utf-8"? >
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="2000"
        android:fillAfter="true"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="360">
</rotate>Copy the code

Zoom view_anim_scale. XML:

<?xml version="1.0" encoding="utf-8"? >
<scale xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="2000"
       android:fromXScale="1.0"
       android:fromYScale="1.0"
       android:pivotX="50%"
       android:pivotY="50%"
       android:toXScale="0.5"
       android:toYScale="0.5">
</scale>Copy the code

Translation view_anim_translate. XML:

<?xml version="1.0" encoding="utf-8"? >
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="2000"
           android:fromXDelta="0"
           android:fromYDelta="0"
           android:toXDelta="100%"
           android:toYDelta="100%">
</translate>Copy the code

The values of the Android :pivotX and Android :pivotY attributes of the rotate and Scale animations and the Android :toXDelta and Android :toYDelta attributes of the Translate animations can be all values, percentages, and percentages p, for example: 50, 50%, and 50%P represent different meanings. 50 represents the position 50px in the positive direction of the coordinate axis (X-axis to the right, Y-axis to the bottom) starting from the upper left corner of the View. 50% represents the position at 50% of the width or height of the View in the positive direction (X-axis to the right, Y-axis down), starting from the upper left corner of the View; 50%p represents the position (p relative to ParentView) at 50% of the width or height of the parent control in the positive direction (x-right, y-down) of the coordinate axis, starting from the upper left corner of View.





“50” position





“50%” position





“50% p” position

By defining an XML animation resource file, call it in your Activity:

public void clickToAlpha(View view) {
    Animation alphaAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_alpha);
    mTargetView.startAnimation(alphaAnim);
}

public void clickToRotate(View view) {
    Animation rotateAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_rotate);
    mTargetView.startAnimation(rotateAnim);
}

public void clickToScale(View view) {
    Animation scaleAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_scale);
    mTargetView.startAnimation(scaleAnim);
}

public void clickToTranslate(View view) {
    Animation translateAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_translate);
    mTargetView.startAnimation(translateAnim);
}

public void clickToSet(View view) {
    Animation setAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.view_anim_set);
    mTargetView.startAnimation(setAnim);
}Copy the code
2.Java code to achieve View animation

Veiw animation can also be implemented directly in Java code in ordinary business logic, Android system provides us with AlphaAnimation, RotateAnimation, ScaleAnimation, TranslateAnimation four animation classes to achieve View gradient, rotation, zoom, pan animation respectively. The gradient:

public void clickToAlpha(View view) {
    AlphaAnimation alphaAnimation = new AlphaAnimation(1.0);
    alphaAnimation.setDuration(2000);
    mTargetView.startAnimation(alphaAnimation);
}Copy the code

Rotation:

public void clickToRotate(View view) {
    RotateAnimation rotateAnimation = new RotateAnimation(
            0.360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation.setDuration(2000);
    mTargetView.startAnimation(rotateAnimation);
}Copy the code

Zoom:

public void clickToScale(View view) {
    ScaleAnimation scaleAnimation = new ScaleAnimation(
            1.0.5f,
            1.0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(2000);
    mTargetView.startAnimation(scaleAnimation);
}Copy the code

Translation:

public void clickToTranslate(View view) {
    TranslateAnimation translateAnimation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1,
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1);
    translateAnimation.setDuration(2000);
    mTargetView.startAnimation(translateAnimation);
}Copy the code

Combination:

public void clickToSet(View view) {
    AlphaAnimation alphaAnimation = new AlphaAnimation(1.0);
    alphaAnimation.setDuration(2000);

    RotateAnimation rotateAnimation = new RotateAnimation(
            0.360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation.setDuration(2000);

    ScaleAnimation scaleAnimation = new ScaleAnimation(
            1.0.5f,
            1.0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(2000);

    TranslateAnimation translateAnimation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1,
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1);
    translateAnimation.setDuration(2000);

    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(alphaAnimation);
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(scaleAnimation);
    animationSet.addAnimation(translateAnimation);

    mTargetView.startAnimation(animationSet);
}Copy the code




View animation effect

View animation can set a listener for animation execution:

animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        // Start animation
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // End of animation
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        // The animation repeats}});Copy the code

Listeners can be set up to do some additional business logic at the beginning, end, and repeat of animation execution.


Second, property animation

The so-called attribute animation is to change the properties of the Object to achieve the animation process. Property animation is an extension of View animation, through which you can achieve more beautiful animation effects. And property animation is not just for View, it can be for any object. The effect of property animation is to dynamically change the value of one property of an object to another within a specified period of time.

1.ObjectAnimator

ObjectAnimator is the most commonly used property animation execution class.

private void startJavaPropertyAnimator() {
    ObjectAnimator
            .ofFloat(mImageView, "rotationY".0f, 360f)
            .setDuration(2000)
            .start(a); }Copy the code

The above code changes the rotationY property value of mImageView from 0F to 360F in 2000ms with ObjectAnimator.





ObjectAnimator implements property animation

ObjectAnimtor can use static methods such as ofInt, ofFloat, and ofObject to construct animation objects by passing in the target Object, property field, property start value, property intermediate value, property end value, etc. In the process of animation update, the setter method of object property is constantly called to change the property value, and the animation process is constantly redrawn. If no start value is given, the system will use reflection to get the initial value of the Object as the start value of the animation. Property animations can also be defined in an XML file. Again, create an Animator folder in the project’s RES directory, and place objectAnimator animations defined in the XML file in this folder.

Property_animator. XML:

<?xml version="1.0" encoding="utf-8"? >
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="2000"
                android:propertyName="rotationY"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType">
</objectAnimator>Copy the code

Java code calls:

private void startXmlPropertyAnimator(a) {
    Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), 
    R.animator.property_animator);
    animator.setTarget(mImageView);
    animator.start();
}Copy the code

The end result looks like the image above. Attribute animations can also be used in combination. Both the AnimatorSet class and the SET tag of an XML file can change multiple attributes of an object at the same time for richer animations. Create an AnimatorSet with AnimatorSet:

private void startJavaPropertyAnimatorSet() {
    Animator scaleXAnimator = ObjectAnimator.ofFloat(mImageView, "scaleX".1.0.5f);
    scaleXAnimator.setDuration(2000);
    Animator scaleYAnimator = ObjectAnimator.ofFloat(mImageView, "scaleY".1.0.5f);
    scaleYAnimator.setDuration(2000);
    Animator rotationXAnimator = ObjectAnimator.ofFloat(mImageView, "rotationX".0.360);
    rotationXAnimator.setDuration(2000);
    Animator rotationYAnimator = ObjectAnimator.ofFloat(mImageView, "rotationY".0.360);
    rotationYAnimator.setDuration(2000);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(scaleXAnimator)
            .with(scaleYAnimator)
            .before(rotationXAnimator)
            .after(rotationYAnimator);
    animatorSet.start();
}Copy the code

AnimatorSet can combine multiple attribute animations using the methods before, with, and after, with with being executed simultaneously with a given animation, before before, and after after.





AnimatorSet builds a property animation set

Define the property animation set as an XML file: property_animator_set.xml

<?xml version="1.0" encoding="utf-8"? >
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="2000"
        android:propertyName="scaleX"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType"/>
    <objectAnimator
        android:duration="2000"
        android:propertyName="scaleY"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType"/>
    <objectAnimator
        android:duration="2000"
        android:propertyName="alpha"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType"/>
</set>Copy the code

Call the property animation set in Java code:

private void startxmlPropertyAnimatorSet(a) {
    Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), 
    R.animator.property_animator_set);
    animator.setTarget(mImageView);
    animator.start();
}Copy the code




XML defines a property animation set

Similarly, property animations can also add animation execution listeners:

animator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        // Start animation
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        // End of animation
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        // Animation cancelled
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        // The animation repeats}});Copy the code

Some other logical business can be done when listening for the property animation to start, end, cancel, and repeat.

2.ValueAnimator

ValueAnimator is the parent of ObjectAnimator, which inherits from Animator. ValueAnimaotor also provides static methods such as ofInt, ofFloat, and ofObject that take the start, middle, and end values of the animation process to construct an animation object. ValueAnimator can look at a value changer that changes a target value from a given start value to a given end value in a given amount of time. When using ValueAnimator, you usually need to add a listener for animation updates. In the listener, you can fetch every animation value during execution.

private void startValueAnimator() {
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.1);
    valueAnimator.setDuration(300);
    valueAnimator.start();
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // The animation value in the process of animation update can be associated with the object properties according to the change of the animation value, to achieve attribute animation
            float value = (float) animation.getAnimatedValue();
            Log.d("ValueAnimator"."Animation value:" + value); }}); }Copy the code

Change of animation value from 0 to 1 in 300ms log:

02-25 2316:: 57.586 D/ValueAnimator: Animation value:0. 0
02-25 2316:: 57.596 D/ValueAnimator: Animation value:0007902175.
02-25 2316:: 57.616 D/ValueAnimator: Animation value:0029559612.
02-25 2316:: 57.636 D/ValueAnimator: Animation value:0066987276.
02-25 2316:: 57.646 D/ValueAnimator: Animation value:0118102014.
02-25 2316:: 57.666 D/ValueAnimator: Animation value:018128797.
02-25 2316:: 57.686 D/ValueAnimator: Animation value:02545482.
02-25 2316:: 57.706 D/ValueAnimator: Animation value:033063102.
02-25 2316:: 57.716 D/ValueAnimator: Animation value:04166157.
02-25 2316:: 57.736 D/ValueAnimator: Animation value:05052359.
02-25 2316:: 57.746 D/ValueAnimator: Animation value:05936906.
02-25 2316:: 57.766 D/ValueAnimator: Animation value:067918396.
02-25 2316:: 57.786 D/ValueAnimator: Animation value:07545208.
02-25 2316:: 57.796 D/ValueAnimator: Animation value:082671034.
02-25 2316:: 57.826 D/ValueAnimator: Animation value:088857293.
02-25 2316:: 57.836 D/ValueAnimator: Animation value:093815327.
02-25 2316:: 57.856 D/ValueAnimator: Animation value:09721882.
02-25 2316:: 57.876 D/ValueAnimator: Animation value:099384415.
02-25 2316:: 57.886 D/ValueAnimator: Animation value:1. 0Copy the code

ValueAnimator is typically used in conjunction with the update listener AnimatorUpdateListener, most often when customizing controls. Below is a custom ValueAnimator control to implement the animation on/off effect. Expanded_veiw. XML:

<?xml version="1.0" encoding="utf-8"? >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="@android:color/white"
              android:divider="@drawable/divider"
              android:orientation="vertical"
              android:showDividers="middle">

    <LinearLayout
        android:id="@+id/ll_expanded_question"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:gravity="center_vertical">

        <TextView
            android:id="@+id/tv_expanded_question"
            android:layout_width="0dp"
            android:layout_height="48dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:padding="8dp"
            android:text="How do you read a difficult book?"
            android:textColor="# 999999"
            android:textSize="16sp"/>

        <ImageView
            android:id="@+id/iv_expanded_indicator"
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_marginRight="16dp"
            android:src="@drawable/img_up"/>

    </LinearLayout>


    <TextView
        android:id="@+id/tv_expanded_answer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="Read it several times. It's really useful. Watch it at least three times. Start at the beginning, look at the middle, start again, look a little bit more, and start again at the beginning, it is advisable to start again near the end."
        android:textColor="# 999999"
        android:textSize="16sp"/>
</LinearLayout>Copy the code
public class ExpandedView extends FrameLayout {

    private TextView mTvAnswer;
    private boolean isClosed;
    private ImageView mIvIndicator;

    public ExpandedView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.expanded_view, this.true);
        LinearLayout llQuestion = (LinearLayout) view.findViewById(R.id.ll_expanded_question);
        llQuestion.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) { anim(); }}); mTvAnswer = (TextView) view.findViewById(R.id.tv_expanded_answer); mIvIndicator = (ImageView) view.findViewById(R.id.iv_expanded_indicator); }private void anim(a) {
        // The indicator rotates
        ValueAnimator valueAnimator1 = isClosed
                ? ValueAnimator.ofFloat(180.0)
                : ValueAnimator.ofFloat(0, 180);
        valueAnimator1.setDuration(500);
        valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue(); mIvIndicator.setRotation(value); }}); valueAnimator1.start();// Open open close operation
        final int answerHeight = mTvAnswer.getMeasuredHeight();
        ValueAnimator valueAnimator2 = isClosed
                ? ValueAnimator.ofInt(-answerHeight, 0)
                : ValueAnimator.ofInt(0, -answerHeight);
        valueAnimator2.setDuration(500);
        final MarginLayoutParams params = (MarginLayoutParams) mTvAnswer.getLayoutParams();
        valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue(); params.bottomMargin = value; mTvAnswer.setLayoutParams(params); }}); valueAnimator2.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {}@Override
            public void onAnimationEnd(Animator animation) { isClosed = ! isClosed; }@Override
            public void onAnimationCancel(Animator animation) {}@Override
            public void onAnimationRepeat(Animator animation) {}}); valueAnimator2.start(); }}Copy the code




ValueAnimator Custom control effect diagram

3.TypeEvaluator

ObjectAnimator and ValueAnimator both have ofObject methods that pass in a TypeEvaluator parameter. TypeEvaluator is an interface that contains only one abstract method:

public T evaluate(float fraction, T startValue, T endValue);Copy the code

When the ofInt method is called, there is an IntEvaluator that implements the TypeEvaluator interface. The source code is very simple:

public Integer evaluate(float fraction.Integer startValue, Integer endValue) {
    int startInt = startValue;
    return (int)(startInt + fraction * (endValue - startInt));
}Copy the code

The fraction range is 0 to 1, indicating how complete the animation is during execution. The generic T is the property type for animation execution. To animate complex objects using property animations, we need to customize TypeEvaluator to implement animation logic. Let’s define an object

public class Circle {

    private int raduis;         / / radius
    private int color;          / / color
    private int elevation;      / / height

    public Circle(int raduis, int color, int elevation) {
        this.raduis = raduis;
        this.color = color;
        this.elevation = elevation;
    }

    public int getRaduis() {
        return raduis;
    }

    public void setRaduis(int raduis) {
        this.raduis = raduis;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getElevation() {
        return elevation;
    }

    public void setElevation(int elevation) {
        this.elevation = elevation; }}Copy the code

Custom control CircleView with Circle as one of its properties:

public class CircleView extends View {
    private Circle circle;
    private Paint mPaint;

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        circle = new Circle(168, Color.RED, 0);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        setElevation(circle.getElevation());
        mPaint.setColor(circle.getColor());
        canvas.drawCircle(getMeasuredHeight() / 2, getMeasuredHeight() / 2, circle.getRaduis(), mPaint);
    }

    public void setCircle(Circle circle) {
        this.circle = circle;
        postInvalidate();
    }

    public Circle getCircle(a) {
        returncircle; }}Copy the code

ObjectAnimator use:

private void start1() {
    Circle startCircle = new Circle(168, Color.RED, 0);
    Circle middleCircle = new Circle(300, Color.GREEN, 15);
    Circle endCircle = new Circle(450, Color.BLUE, 30);
    ObjectAnimator.ofObject(mCircleView, "circle".new CircleEvaluator(), startCircle, middleCircle, endCircle)
            .setDuration(5000)
            .start();
}Copy the code

ValueAnimator use:

private void start2(a) {
    Circle startCircle = new Circle(168, Color.RED, 0);
    Circle middleCircle = new Circle(300, Color.GREEN, 15);
    Circle endCircle = new Circle(450, Color.BLUE, 30);
    ValueAnimator valueAnimator = ValueAnimator.ofObject(new CircleEvaluator(), startCircle, middleCircle, endCircle);
    valueAnimator.setDuration(5000);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) { Circle circle = (Circle) animation.getAnimatedValue(); mCircleView.setCircle(circle); }}); valueAnimator.start(); }Copy the code




Custom TypeEvaluator renderings

The property name must be the same as the string behind the name of the getter and setter methods. For example, the getCircle and setCircle methods are getCircle and setCircle respectively. The property name must be circle.


Three, frame animation

Frame animation requires developers to develop each frame of animation, and the system plays pictures frame by frame.

private void start1() {
    AnimationDrawable ad = new AnimationDrawable();
    for (int i = 0; i < 7; i++) {
        Drawable drawable = getResources().getDrawable(getResources().getIdentifier("ic_fingerprint_" + i, "drawable", getPackageName()));
        ad.addFrame(drawable, 100);
    }
    ad.setOneShot(false);
    mImageView.setImageDrawable(ad);
    ad.start();
}Copy the code

Frame animation can also be configured in an XML file. Create an animation-list tag in the project drawable directory:

<?xml version="1.0" encoding="utf-8"? >
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
<item android:drawable="@drawable/ic_fingerprint_0" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_1" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_2" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_3" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_4" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_5" android:duration="100"/>
<item android:drawable="@drawable/ic_fingerprint_6" android:duration="100"/>
</animation-list>Copy the code
private void start2() {
    mImageView.setImageResource(R.drawable.frame_anim);
    AnimationDrawable animationDrawable = (AnimationDrawable) mImageView.getDrawable();
    animationDrawable.start();
}Copy the code

The Android: onShot attribute and setOneShot method indicate whether to execute only once. An AnimationDrawable is actually a Drawable animation, and the animation will always redraw each frame of the Drawable for dynamic playback.





Frame animation renderings