Android attribute animation to achieve the cool effect of frame animation

Recently, the company let a very dazzling animation, the first thought is to use frame animation, and then think of too many pictures, using frame animation is too performance-consuming, because there are many pictures are likely to appear oom, so decided to use attribute animation to achieve, the following is the animation effect map.

        :

Here are some ideas:

First, write the layout file according to the effect of the animation.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:ignore="RtlHardcoded,ContentDescription">

    <!--way-->
    <ImageView
        android:id="@+id/iv_way1"
        android:layout_width="56dp"
        android:layout_height="155dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="110dp"
        android:layout_marginRight="50dp"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_way2"
        android:layout_width="56dp"
        android:layout_height="162dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="200dp"
        android:layout_marginRight="35dp"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_way3"
        android:layout_width="61dp"
        android:layout_height="157dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="160dp"
        android:layout_marginLeft="20dp"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_way4"
        android:layout_width="104dp"
        android:layout_height="111dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="110dp"
        android:layout_marginLeft="20dp"
        android:visibility="gone" />

    <!--fire-->
    <ImageView
        android:id="@+id/iv_fire1"
        android:layout_width="119dp"
        android:layout_height="119dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="200dp"
        android:layout_marginRight="62dp"
        android:scaleType="centerCrop"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_fire2"
        android:layout_width="97dp"
        android:layout_height="97dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="310dp"
        android:layout_marginRight="50dp"
        android:scaleType="centerCrop"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_fire3"
        android:layout_width="245dp"
        android:layout_height="245dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="180dp"
        android:layout_marginLeft="40dp"
        android:scaleType="centerCrop"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_fire4"
        android:layout_width="103dp"
        android:layout_height="103dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="168dp"
        android:layout_marginLeft="65dp"
        android:scaleType="centerCrop"
        android:visibility="gone" />

    <!--castle-->
    <LinearLayout
        android:id="@+id/ll_castle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center|bottom"
        android:layout_marginBottom="30dp"
        android:orientation="vertical"
        android:visibility="gone">

        <include
            layout="@layout/view_gift_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="20dp" />

        <ImageView
            android:id="@+id/iv_castle"
            android:layout_width="207dp"
            android:layout_height="192dp"
            android:scaleType="centerCrop"
            android:visibility="gone" />
    </LinearLayout>


    <!--light-->
    <ImageView
        android:id="@+id/iv_light1"
        android:layout_width="74dp"
        android:layout_height="254dp"
        android:layout_gravity="bottom|left"
        android:layout_marginBottom="60dp"
        android:layout_marginLeft="-37dp"
        android:rotation="50"
        android:scaleType="centerCrop"
        android:transformPivotX="37dp"
        android:transformPivotY="254dp"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_light2"
        android:layout_width="74dp"
        android:layout_height="254dp"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginRight="80dp"
        android:rotation="30"
        android:scaleType="centerCrop"
        android:transformPivotX="37dp"
        android:transformPivotY="254dp"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_light3"
        android:layout_width="74dp"
        android:layout_height="254dp"
        android:layout_gravity="bottom|center"
        android:layout_marginLeft="80dp"
        android:rotation="-30"
        android:scaleType="centerCrop"
        android:transformPivotX="37dp"
        android:transformPivotY="254dp"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_light4"
        android:layout_width="74dp"
        android:layout_height="254dp"
        android:layout_gravity="bottom|right"
        android:layout_marginBottom="60dp"
        android:layout_marginRight="-40dp"
        android:rotation="-50"
        android:scaleType="centerCrop"
        android:transformPivotX="40dp"
        android:transformPivotY="254dp"
        android:visibility="gone" />
</FrameLayout>
The above is an xml layout based on the animation effect. The layout is divided into four parts: 1 fireworks path, 2 fireworks, 3 castles, 4 lights. These four parts are designed according to the animation effect.

Next, we use attribute animation to implement it. Before using attribute animation, we need to know several classes and some methods of these classes.

Animator

      This is the superclass for classes which provide basic support for animations which can be started, ended, and have AnimatorListeners added to them.

This is a superclass that provides basic animation support to start, end and add animation listeners.

Direct subclasses: AnimatorSet, Value Animator

Indirect subclasses: ObjectAnimator, Time Animator

Internal classes: Animator.AnimatorListener,Animator.AnimatorPauseListener

Constant: DURATION_INFINITE This is a long type of parameter used to define the infinite duration of animation execution, such as when animation is repeated

Constructor: Animator()

The following are public methods:

       1 addListener(Animator.AnimatorListener listener) No return value, it is used to add a monitor for the entire life cycle of animation execution, can monitor the beginning of animation, repetition, end and so on.

       2 addPauseListener(Animator.AnimatorPauseListener listener) No return value, it is used to add a paused listener, which can be triggered when the animation pauses. _

       3 cancel() No return value, it is used to cancel the animation, it is different from end(), it causes the animation to stop in its own way, by sending a onAnimationCancel(Animator) Closely following onAnimationEnd(Animator) After that.     

       4 clone() The return value is Animator, which is used to create an animated object to return.

       5 end() No return value, it is used to end the animation by assigning the end parameters to the properties that execute the animation, and then by calling onAnimationEnd(Animator) To end the animation.

       6 getDuration() Abstract method, the return value is long type, it can get the execution duration of the animation.

There are a lot of ways, so don't write them one by one. Let's go and see for ourselves.^^

ObjectAnimator is an indirect subclass of Animator, which inherits Value Animator and Value Animator inherits Animator.

The following are only some of the methods used, others to see for themselves.

       ofFloat(Object target, String propertyName, float... values) The return value is a static ObjectAnimator object.

      Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to, in which case the start value will be derived from the property being animated and the target object when start() is called for the first time. Two values imply starting and ending values. More than two values imply a starting value, values to animate through along the way, and an ending value (these values will be distributed evenly across the duration of the animation).

Combining with a dictionary, I'll translate Ha, float... values, which is an interesting parameter. It's an array. Usually when we use it, we pass a start value and an end value. String propertyName is the name of the attribute animation to be executed, such as rotation (rotation), alpha (transparency). ScaleX (scaling in x-axis direction), scaleY (scaling in Y-axis direction), x (moving in x-axis direction), y (moving in Y-axis direction). Object target parameter is the object target to execute the animation, that is, view. After understanding these parameters, the whole is easy to translate, meaning to construct and return an animation according to VA. Lue executes. If there is only one value, the value is the value of the animation to be executed. In this case, the starting value will be obtained from the target of the property animation to be executed when the start() method is first called. If there are two values, one is the beginning and the other is the end. If there are more than two values, one is the beginning value, the middle value is the value of the animation along the path, and the last value is the end value.

Three Property Values Holder is a class that can be used in conjunction with Object Animator, Value Animator, to create some complex composite animation.

          ofFloat(String propertyName, float... values) Returns a static Property ValuesHolder object. This method is similar to the object Animator's ofFloat() described above, except that the first target parameter is missing.

Four Animator Set What does it do? The above property value sholder can implement simple combination animation of a single view, such as rotation, zooming and moving at the same time, so this class is more niu, it can combine all view animations, and achieve more complex combination animation.

      play(Animator anim) Let's start with this method, whose return value is AnimatorSet.Builder Through this builder, different animations can be combined in chronological order, that is to say, it can be used to establish some constraints of animation execution.

5 AnimatorSet.Builder 

     after(long The return value is AnimatorSet.Builder Object, through which animator in play(animator) can be delay ed before execution.

     after(Animator anim) The return value is AnimatorSet.Builder Object, which enables animator in play(animator) to be in after(Animator) The anim in anim is executed after execution.

     before(Animator anim) The return value is AnimatorSet.Builder Object, which enables animator in play(animator) to be in before(Animator) The anim in anim is executed before it is executed.

  with(Animator anim) The return value is AnimatorSet.Builder Object, which enables animator and with in play(animator) The anim in anim is executed simultaneously.

After introducing some of the classes used and some of the methods in these classes, we will begin to knock on the code implementation.

The animation is divided into four parts, fireworks, fireworks path, castle, lighting, we can put fireworks and paths together to achieve.

Firstly, the animation of fireworks is realized.

    private Animator getFireWorkAnimator(final ImageView way, final int wayHeight, final ImageView fire) {
        ValueAnimator wayAni = ValueAnimator.ofFloat(0f, ViewUtil.dp2Px(way.getContext(), wayHeight));
        wayAni.setDuration(300);
        wayAni.addListener(new ViewShowListener(way));
        wayAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            int lastHeight;

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float value = (Float) valueAnimator.getAnimatedValue();
                way.getLayoutParams().height = value.intValue();
                lastHeight = value.intValue();
                way.requestLayout();
            }
        });

        ObjectAnimator wayScaleAni = ObjectAnimator.ofFloat(way, "alpha", 1, 0.2f);
        wayScaleAni.setDuration(500);

        PropertyValuesHolder alphaValue = PropertyValuesHolder.ofFloat("alpha", 0, 1);
        PropertyValuesHolder scaleXValue = PropertyValuesHolder.ofFloat("scaleX", 0, 1);
        PropertyValuesHolder scaleYValue = PropertyValuesHolder.ofFloat("scaleY", 0, 1);
        ObjectAnimator fireAni = ObjectAnimator.ofPropertyValuesHolder(fire, alphaValue, scaleXValue, scaleYValue);
        fireAni.setDuration(600);
        fireAni.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                fire.setVisibility(View.VISIBLE);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        way.setVisibility(View.GONE);
                    }
                }, 300);
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        fire.setVisibility(View.GONE);
                    }
                }, 300);
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

        AnimatorSet set = new AnimatorSet();
        set.play(wayAni).with(wayScaleAni);
        set.play(wayScaleAni).before(fireAni);
        set.play(fireAni);
        return set;
    }
Then animate the castle:

       

   private Animator getCastleShowingAnimator() {
        float castleSY = ViewUtil.getScreenHeight(context) + ViewUtil.dp2Px(context, 240);
        float castleDY = ViewUtil.getScreenHeight(context) - ViewUtil.dp2Px(context, 240) - ViewUtil.dp2Px(context, 30);
        PropertyValuesHolder castleX1 = PropertyValuesHolder.ofFloat("y", castleSY, castleDY);

        PropertyValuesHolder castleScaleX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1.0f);
        PropertyValuesHolder castleScaleY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1.0f);

        ObjectAnimator castleAni = ObjectAnimator.ofPropertyValuesHolder(llCastle, castleX1, castleScaleX, castleScaleY);
        castleAni.addListener(new ViewShowListener(ivCastle));
        castleAni.addListener(new ViewShowListener(llCastle));
        castleAni.setInterpolator(new OvershootInterpolator());
        castleAni.setDuration(1000);
        return castleAni;
    }
The animation of the castle is simpler. Finally, we realize the animation of lighting.

      

   private Animator getLightersAnimator() {
        Animator light1 = getLighterAnimator(ivLight1, -15, 20, -30, 50);
        Animator light2 = getLighterAnimator(ivLight2, 0, -50, 30, 30);
        Animator light3 = getLighterAnimator(ivLight3, 0, 50, -30, -30);
        Animator light4 = getLighterAnimator(ivLight4, -15, -30, 10, -55);
        AnimatorSet set = new AnimatorSet();
        set.play(light1).with(light4);
        set.play(light4);
        set.play(light2).with(light3).after(200);
        return set;
    }

    private Animator getLighterAnimator(ImageView lighter, int rotation1, int rotation2, int rotation3, int rotation4) {
        Animator ani1 = getLighterAnimator(lighter, rotation1, rotation2);
        Animator ani2 = getLighterAnimator(lighter, rotation2, rotation3);
        Animator ani3 = getLighterAnimator(lighter, rotation3, rotation4);
        AnimatorSet set = new AnimatorSet();
        set.addListener(new ViewShowListener(lighter));
        set.play(ani1).before(ani2);
        set.play(ani2).before(ani3);
        return set;
    }

    private Animator getLighterAnimator(ImageView lighter, int origin, int destination) {
        ObjectAnimator lighterAni = ObjectAnimator.ofFloat(lighter, "rotation", origin, destination);
        lighterAni.setDuration(800);
        return lighterAni;
    }

The following is a custom animator listener:

       

    class ViewShowListener implements Animator.AnimatorListener {
        private View view;

        public ViewShowListener(View view) {
            this.view = view;
        }

        @Override
        public void onAnimationStart(Animator animator) {
            view.setVisibility(View.VISIBLE);
        }

        @Override
        public void onAnimationEnd(Animator animator) {

        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    }

Basically, the effect of animation is achieved. There will be many details of animation in the process of doing, such as the execution time of each animation, execution path, which should be discussed with the ui of animation design. It will take two days to make this animation.

Next, I will post the completed code, and then the demo download address.

package com.guoliuya.propertyanimationdemo;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.guoliuya.util.ViewUtil;

public class MainActivity extends AppCompatActivity {
    private ImageView ivCastle, ivLight1, ivLight2, ivLight3, ivLight4, ivWay1, ivWay2, ivWay3, ivWay4, ivFire1, ivFire2, ivFire3, ivFire4;
    private LinearLayout llCastle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        setView();
        findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                doAnimation();
            }
        });
    }

    private void setView() {
        ivCastle.setImageResource(R.mipmap.cb);
        ivWay1.setImageResource(R.mipmap.f1);
        ivWay2.setImageResource(R.mipmap.f2);
        ivWay3.setImageResource(R.mipmap.f3);
        ivWay4.setImageResource(R.mipmap.f4);
        ivFire1.setImageResource(R.mipmap.y1);
        ivFire2.setImageResource(R.mipmap.y2);
        ivFire3.setImageResource(R.mipmap.y3);
        ivFire4.setImageResource(R.mipmap.y4);
        ivLight1.setImageResource(R.mipmap.g1);
        ivLight2.setImageResource(R.mipmap.g2);
        ivLight3.setImageResource(R.mipmap.g3);
        ivLight4.setImageResource(R.mipmap.g4);
    }

    private void initView() {
        ivCastle = (ImageView) findViewById(R.id.iv_castle);
        llCastle = (LinearLayout) findViewById(R.id.ll_castle);
        ivLight1 = (ImageView) findViewById(R.id.iv_light1);
        ivLight2 = (ImageView) findViewById(R.id.iv_light2);
        ivLight3 = (ImageView) findViewById(R.id.iv_light3);
        ivLight4 = (ImageView) findViewById(R.id.iv_light4);
        ivWay1 = (ImageView) findViewById(R.id.iv_way1);
        ivWay2 = (ImageView) findViewById(R.id.iv_way2);
        ivWay3 = (ImageView) findViewById(R.id.iv_way3);
        ivWay4 = (ImageView) findViewById(R.id.iv_way4);
        ivFire1 = (ImageView) findViewById(R.id.iv_fire1);
        ivFire2 = (ImageView) findViewById(R.id.iv_fire2);
        ivFire3 = (ImageView) findViewById(R.id.iv_fire3);
        ivFire4 = (ImageView) findViewById(R.id.iv_fire4);
    }

    private void doAnimation() {

        Animator lighterAni = getLightersAnimator();
        Animator castleAni = getCastleShowingAnimator();
        Animator fireworks = getFireworksAnimator();
        Animator fireworks2 = getFireworksAnimator();

        AnimatorSet set = new AnimatorSet();
        set.play(lighterAni).with(castleAni);
        set.play(castleAni).after(1100).before(fireworks);
        set.play(fireworks).after(1400).before(fireworks2);
        set.play(fireworks2).after(2200);
        set.start();
    }

    private Animator getLightersAnimator() {
        Animator light1 = getLighterAnimator(ivLight1, -15, 20, -30, 50);
        Animator light2 = getLighterAnimator(ivLight2, 0, -50, 30, 30);
        Animator light3 = getLighterAnimator(ivLight3, 0, 50, -30, -30);
        Animator light4 = getLighterAnimator(ivLight4, -15, -30, 10, -55);
        AnimatorSet set = new AnimatorSet();
        set.play(light1).with(light4);
        set.play(light4);
        set.play(light2).with(light3).after(200);
        return set;
    }

    private Animator getLighterAnimator(ImageView lighter, int rotation1, int rotation2, int rotation3,
            int rotation4) {
        Animator ani1 = getLighterAnimator(lighter, rotation1, rotation2);
        Animator ani2 = getLighterAnimator(lighter, rotation2, rotation3);
        Animator ani3 = getLighterAnimator(lighter, rotation3, rotation4);
        AnimatorSet set = new AnimatorSet();
        set.addListener(new ViewShowListener(lighter));
        set.play(ani1).before(ani2);
        set.play(ani2).before(ani3);
        return set;
    }

    private Animator getLighterAnimator(ImageView lighter, int origin, int destination) {
        ObjectAnimator lighterAni = ObjectAnimator.ofFloat(lighter, "rotation", origin, destination);
        lighterAni.setDuration(800);
        return lighterAni;
    }

    private Animator getCastleShowingAnimator() {
        float castleSY = ViewUtil.getScreenHeight(this) + ViewUtil.dp2Px(this, 240);
        float castleDY =
                ViewUtil.getScreenHeight(this) - ViewUtil.dp2Px(this, 240) - ViewUtil.dp2Px(this, 30);
        PropertyValuesHolder castleX1 = PropertyValuesHolder.ofFloat("y", castleSY, castleDY);

        PropertyValuesHolder castleScaleX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1.0f);
        PropertyValuesHolder castleScaleY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1.0f);

        ObjectAnimator castleAni = ObjectAnimator
                .ofPropertyValuesHolder(llCastle, castleX1, castleScaleX, castleScaleY);
        castleAni.addListener(new ViewShowListener(ivCastle));
        castleAni.addListener(new ViewShowListener(llCastle));
        castleAni.setInterpolator(new OvershootInterpolator());
        castleAni.setDuration(1000);
        return castleAni;
    }

    private Animator getFireworksAnimator() {
        Animator fireworkAni1 = getFireWorkAnimator(ivWay1, 155, ivFire1);
        Animator fireworkAni2 = getFireWorkAnimator(ivWay2, 162, ivFire2);
        Animator fireworkAni3 = getFireWorkAnimator(ivWay3, 157, ivFire3);
        Animator fireworkAni4 = getFireWorkAnimator(ivWay4, 111, ivFire4);
        AnimatorSet set = new AnimatorSet();
        set.play(fireworkAni1).with(fireworkAni2);
        set.play(fireworkAni2).after(200);
        set.play(fireworkAni4).with(fireworkAni3);
        set.play(fireworkAni3).after(200);
        return set;
    }

    private Animator getFireWorkAnimator(final ImageView way, final int wayHeight, final ImageView fire) {
        ValueAnimator wayAni = ValueAnimator.ofFloat(0f, ViewUtil.dp2Px(way.getContext(), wayHeight));
        wayAni.setDuration(300);
        wayAni.addListener(new ViewShowListener(way));
        wayAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            int lastHeight;

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float value = (Float) valueAnimator.getAnimatedValue();
                way.getLayoutParams().height = value.intValue();
                lastHeight = value.intValue();
                way.requestLayout();
            }
        });

        ObjectAnimator wayScaleAni = ObjectAnimator.ofFloat(way, "alpha", 1, 0.2f);
        wayScaleAni.setDuration(500);

        PropertyValuesHolder alphaValue = PropertyValuesHolder.ofFloat("alpha", 0, 1);
        PropertyValuesHolder scaleXValue = PropertyValuesHolder.ofFloat("scaleX", 0, 1);
        PropertyValuesHolder scaleYValue = PropertyValuesHolder.ofFloat("scaleY", 0, 1);
        ObjectAnimator fireAni = ObjectAnimator
                .ofPropertyValuesHolder(fire, alphaValue, scaleXValue, scaleYValue);
        fireAni.setDuration(600);
        fireAni.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                fire.setVisibility(View.VISIBLE);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        way.setVisibility(View.GONE);
                    }
                }, 300);
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        fire.setVisibility(View.GONE);
                    }
                }, 300);
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

        AnimatorSet set = new AnimatorSet();
        set.play(wayAni).with(wayScaleAni);
        set.play(wayScaleAni).before(fireAni);
        set.play(fireAni);
        return set;
    }

    class ViewShowListener implements Animator.AnimatorListener {
        private View view;

        public ViewShowListener(View view) {
            this.view = view;
        }

        @Override
        public void onAnimationStart(Animator animator) {
            view.setVisibility(View.VISIBLE);
        }

        @Override
        public void onAnimationEnd(Animator animator) {

        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    }
}

demo Download Address: https://github.com/guoliuya/Property Animation demo

Tags: Android Attribute xml encoding

Posted on Tue, 09 Jul 2019 18:14:37 -0400 by imran.rajani