Zero, Preface:Knowledge points

  • Understanding and using ValueAnimator
  • Customizing and using the TypeEvaluator
  • Custom and usage of TimeInterpolator
  • Path in combination with Animator
  • ObjectAnimator customization and use
  • The use of TimeAnimator
  • AnimatorSet Use of an animation set
  • Introduction and use of listeners of the Animator family
  • Use of the Animator family in XML

The Animator system is not complex, but the internal implementation is quite complex. Many classes have been buried in the bottom for years, not to see the light of day such as: PropertyValuesHolder and its subclasses, Keyframes family, Keyframe family, KeyframeSet family today tried to read the source code, basically read the muddled, the general idea is to grasp


Section 1: Use of ValueAnimator

First, simple use

0.Animator Family simple understanding:

Animator is an abstract class. It is not available. You can only find its subclasses


1. Here is the simplest use of ValueAnimator
ValueAnimator animator = ValueAnimator.ofInt(0, 10);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.e(TAG, animation.getAnimatedValue()+"-"); }}); animator.start();Copy the code

Print result analysis:

2018-12-26 12:04:09.290 ~ 2018-12-26 12:04:09.584---->584-290=294 The default duration is 300(defined in the source code), basically the same, The basic purpose of ValueAnimator is to constantly call back to the onAnimationUpdate method during this time and to change the animation value from a predetermined range of 0 to 10Copy the code
The 2018-12-26 12:04:09. 290, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 0-2018-12-26 12:04:09. 335, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 0-2018-12-26 12:04:09. 351, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 1-2018-12-26 12:04:09. 373, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 1-2018-12-26 12:04:09. 412, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 3 - the 2018-12-26 12:04:09. 439, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 5-2018-12-26 12:04:09. 450, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 5-2018-12-26 12:04:09. 468, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 6-2018-12-26 12:04:09. 484, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 7-2018-12-26 12:04:09. 502, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 8-2018-12-26 12:04:09. 517, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 8-2018-12-26 12:04:09. 534, 2001-2001 / com. Toly1994. Animator_test E/MainActivity: 9-2018-12-26 12:04:09. 568. 2001-2001 / com toly1994. Animator_test E/MainActivity: 9-2018-12-26 12:04:09. 584. 2001-2001 / com toly1994. Animator_test E/MainActivity: 10 -Copy the code

2. Ideas derived from it

1). Constantly call the onAnimationUpdate callback 2). You can get different values that change regularly in the custom View onAnimationUpdate to refresh the interface and dynamically change the values

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2018/12/26 0026:7:50<br/> * Email: [email protected]<br/> * Description: Public class AnimatorView extends View {private static final String TAG ="AnimatorView"; private Paint mPaint; Private int mRadius = 100; Private ValueAnimator mAnimator; private ValueAnimator mAnimator; Public AnimatorView(Context Context) {this(Context, null); } public AnimatorView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private voidinit() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(0xff94E1F7);

        mAnimator = ValueAnimator.ofInt(100, 300);
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mRadius= (int) animation.getAnimatedValue(); invalidate(); }}); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(400, 400); DrawCircle (0, 0, mRadius, mPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent: "); mAnimator.start(); // Click to open the animationbreak;
            case MotionEvent.ACTION_UP:
        }
        returnsuper.onTouchEvent(event); }}Copy the code

It’s a simple matter of replacing printouts with a refresh view, and the radius is constantly changing


3. Common configuration

Take a look at the difference between RESTART(default) and REVERSE

RESTART REVERSE
mAnimator.setStartDelay(1000); // Set delay manimator.setrepeatCount (2); // Set the number of repetitions. // manimator.setrepeatMode (valueAnimator.restart); 100->300 100->300 manimator.setrepeatMode (valueAnimator.reverse); Manimator.setduration (1000); manimator.setduration (1000); // Set the durationCopy the code

Second,ofArgbwithofObject

Color change Color size

Step 1 Change color:ofArgb

Pass in two colors (start color and end color)

mColorAnimator = ValueAnimator.ofArgb(0xff94E1F7, 0xffF35519); mColorAnimator.setDuration(500); / / set the length mColorAnimator. SetRepeatCount (1); / / set the number of repeat mColorAnimator. SetRepeatMode (ValueAnimator. REVERSE); mColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPaint.setColor((Integer) animation.getAnimatedValue()); invalidate(); }});Copy the code

2. How to change both size and color:

ValueAnimator.ofObject + TypeEvaluator

2.1 First define a class to host the data: Ball(for the sake of brevity, use the public property)
public class Ball {
    public int color;
    public int r;

    public Ball() { } public Ball(int r, int color) { this.color = color; this.r = r; }}Copy the code

Creating a TypeEvaluator

A TypeEvaluator is used to determine how various properties of an object change. Here is an example: Where fraction is the fraction, startValue and endValue are the states of the start and end objects, respectively

public class BallEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(floatfraction, Object startValue, Object endValue) { Ball start = (Ball) startValue; Ball end = (Ball) endValue; Ball Ball = new Ball(); R = (int) (start.r + fraction *(end.r - start.r)); // How the color gradient? ball.color = evaluateColor(fraction, start.color, end.color);returnnull; } /** * evaluateColor(evaluateColor);float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        floatStartA = ((startInt >> 24) &0xff) / 255.0f;floatStartR = ((startInt >> 16) &0xff) / 255.0f;floatStartG = ((startInt >> 8) &0xff) / 255.0f;floatStartB = (startint&0xff) / 255.0f; int endInt = (Integer) endValue;floatEndA = ((endInt >> 24) &0xff) / 255.0f;floatEndR = ((endInt >> 16) &0xff) / 255.0f;floatEndG = ((endInt >> 8) &0xff) / 255.0f;floatEndB = (endInt &0xff) / 255.0f; // convert from sRGB to linear startR = (float() Math. Pow startR, 2.2); startG = (float() Math. Pow startG, 2.2); startB = (float() Math. Pow startB, 2.2); endR = (float() Math. Pow endR, 2.2); endG = (float() Math. Pow endG, 2.2); endB = (float() Math. Pow endB, 2.2); // compute the interpolated colorin linear space
        float a = startA + fraction * (endA - startA);
        float r = startR + fraction * (endR - startR);
        float g = startG + fraction * (endG - startG);
        float b = startB + fraction * (endB - startB);

        // convert back to sRGB inThe [0..255] range a = a * 255.0f; r = (float) math.pow (r, 1.0/2.2) * 255.0f; g = (float) math.pow (g, 1.0/2.2) * 255.0f; b = (float) math.pow (b, 1.0/2.2) * 255.0f;returnMath.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b); }}Copy the code

Look at how the gradient color in the source: ArgbEvaluator getInstance () you can see a computing method of color with bai direct kao used to (I)

public static ValueAnimator ofArgb(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    anim.setEvaluator(ArgbEvaluator.getInstance());
    returnanim; } -- -- -- - > [color method to calculate the evaluate] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- public Object evaluate (floatFraction, Object startValue, Object endValue) {// Calculate the color method details...... }Copy the code

3. Specify the equation motion of the curve using the estimator

The equation is a conic :y=x*x/800 but you can define what you like

public class Ball {
    public int color;
    public int r;
    public int x;
    public int y;

    public Ball() { } public Ball(int r, int color) { this.color = color; this.r = r; } public Ball(int r, int color, int x, int y) { this.color = color; this.r = r; this.x = x; this.y = y; }}Copy the code

Evaluator modification:

public class BallEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(floatfraction, Object startValue, Object endValue) { Ball start = (Ball) startValue; Ball end = (Ball) endValue; Ball ball = new Ball(); ball.color = evaluateColor(fraction, start.color, end.color); ball.r = (int) (start.r + fraction * (end.r - start.r)); ball.x = (int) (start.x + fraction * (end.x - start.x)); ball.y= ball.x*ball.x/800; // The value of y depends on xreturnball; }}Copy the code

AnimatorView

public class AnimatorView extends View {
    private static final String TAG = "AnimatorView";
    private Paint mPaint;
    private int mRadius = 50;
    private int dx;
    private int dy;

    private ValueAnimator mAnimator;
    private ValueAnimator mColorAnimator;
    private ValueAnimator mObjAnimator;

    public AnimatorView(Context context) {
        this(context, null);
    }

    public AnimatorView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(0xff94E1F7); Ball startBall = new Ball(50, 0xff94E1F7,0,0); Ball endBall = new Ball(100, 0xffF35519,500,1000); mObjAnimator = ValueAnimator.ofObject(new BallEvaluator(), startBall, endBall); mObjAnimator.setRepeatMode(ValueAnimator.REVERSE); 100->300 300->100 mobjAnimator.setduration (1000); / / set the length mObjAnimator. SetRepeatCount (1); / / set the number of repeat mObjAnimator. SetRepeatMode (ValueAnimator. REVERSE); mObjAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Ball ball = (Ball) animation.getAnimatedValue();  mRadius = ball.r; mPaint.setColor(ball.color); dx=ball.x; dy=ball.y; Log.e(TAG,"onAnimationUpdate: "+dx+":"+dy); invalidate(); }}); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(dx, dy); canvas.drawCircle(mRadius, mRadius, mRadius, mPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {case MotionEvent.ACTION_DOWN:
                mObjAnimator.start();
                break;
            case MotionEvent.ACTION_UP:
        }

        returnsuper.onTouchEvent(event); }}Copy the code

This is the basic routine. With ofObject, properties can be changed at will. Are you still afraid of animation? In fact, ofInt,ofFloat, and ofArgb just apply the built-in evaluator, which is essentially no different from ofObject. It can be viewed as a simple version of ofObject with a single attribute


Three, interpolator

If the evaluator TypeEvaluator tells you how to run, then the interpolator tells you how fast to run


1. Custom interpolator: Sin type fast then slow

In this case, the input changes from 0 to 1, and the interpolator changes the input

public class D_Sin_Inter implements TimeInterpolator {
    @Override
    public float getInterpolation(floatInput) {// Input is a uniform value from 0 to 1 // a uniform value from 0 to PI/2float rad = (float) (Math.PI/2 * input); // Return the sine of this radian -- the sine curve grows slower and slower between 0 and PI/2, and the ball moves slower and slowerreturn (float) (Math.sin(rad)); }}Copy the code

2. Custom interpolator: Sin type first full and then fast
public class A_Sin_Inter implements TimeInterpolator {
    @Override
    public float getInterpolation(floatInput) {// Input is a uniform value from 0 to 1 // a uniform value from 0 to PI/2float rad = (float) (Math.PI/2 * input+Math.PI/2); // Return the sine of this radian -- the sine curve decreases faster and faster in PI/2~PIreturn (float) (1-(Math.sin(rad))); // Return 1-}}Copy the code

3. Custom interpolator: Log
/** * Author: Zhang Feng Jiete Lay <br/> * Time: 2018/12/26 0026:20:41<br/> * Email: [email protected]<br/> * Description: Public class D_Log_Inter implements TimeInterpolator {@override publicfloat getInterpolation(float input) {
        return (float) (Math.log10(1 + 9 * input)); }}Copy the code

Interpolator is actually based on input processing. The time flow (each refresh interval) is basically constant, and the input uniformly changes from 0 to 1. It is mapped to a set of corresponding relations through input. The return value is y, which is used in code (D_Sin_Inter) LinearInterpolator x=y, but it’s the same thing


4. Elegant implementation test code

Just add them to the name array and interpolator array, and the rest will be handled automatically

public class AnimatorInterView extends View {
    private static final String TAG = "AnimatorView";

    private Paint mPaint;
    private int mRadius = 50;
    private int dx[];
    private String[] mStrings;
    private TimeInterpolator[] mInterpolators;

    public AnimatorInterView(Context context) {
        this(context, null);
    }

    public AnimatorInterView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(0xff94E1F7);
        mPaint.setTextSize(40);
        mStrings = new String[]{"Linear"."Bounce"."AOI"."OI"."D_sin"."D_log"."A_sin"."A_log"}; mInterpolators = new TimeInterpolator[]{ new LinearInterpolator(), new BounceInterpolator(), new AnticipateOvershootInterpolator(), new OvershootInterpolator(), new D_Sin_Inter(), new D_Log_Inter(), new A_Sin_Inter()}; dx = new int[mInterpolators.length]; } private ValueAnimator createAnimator(int index, TimeInterpolator interpolator) { ValueAnimator mAnimator = ValueAnimator.ofInt(0, 800); mAnimator.setRepeatCount(1); Manimator.setrepeatmode (valueAnimator.reverse); Manimator.setduration (3000); manimator.setduration (3000); / / set the length mAnimator. SetInterpolator (interpolator); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { dx[index] = (int) animation.getAnimatedValue(); invalidate(); }});return mAnimator;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < dx.length; i++) {
            canvas.translate(0, 120);
            mPaint.setColor(0xff94E1F7);
            canvas.drawCircle(mRadius + dx[i], mRadius, mRadius, mPaint);
            mPaint.setColor(0xff000000);
            mPaint.setStrokeWidth(4);
            canvas.drawLine(mRadius, mRadius, 800 + mRadius, mRadius, mPaint);
            canvas.drawText(mStrings[i], 800 + 3 * mRadius, mRadius, mPaint);
        }
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                for (int i = 0; i < mInterpolators.length; i++) {
                    createAnimator(i, mInterpolators[i]).start();
                }
                break;
            case MotionEvent.ACTION_UP:
        }
        returnsuper.onTouchEvent(event); }}Copy the code

Interlude: A combination of paths and animators

The core is to use PathMeasure and DashPathEffect to control the length of the Path. For details, see everything you Know and Don’t know about Path on Android

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2018/12/26 0026:7:50<br/> * Email: [email protected]<br/> * Description: Animator and Path */ public Class AnimatorPathView Extends View {private static final String TAG ="AnimatorView";

    private Paint mPaint;
    private Path mPath;
    private PathMeasure pathMeasure;

    public AnimatorPathView(Context context) {
        this(context, null);
    }

    public AnimatorPathView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(0xff94E1F7); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(10); mPaint.setStrokeJoin(Paint.Join.ROUND); MPath = new Path(); mPath = nStarPath(mPath, 8, 250, 160); PathMeasure = new pathMeasure (mPath,false);
    }

    private ValueAnimator createAnimator() { ValueAnimator mAnimator = ValueAnimator.ofInt(0, 800); mAnimator.setRepeatCount(1); Manimator.setrepeatmode (valueAnimator.reverse); Manimator.setduration (3000); manimator.setduration (3000); / / set the length mAnimator. SetInterpolator (new AnticipateOvershootInterpolator ()); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                floatvalue = animation.getAnimatedFraction(); DashPathEffect effect = new DashPathEffect(new)float[]{ pathMeasure.getLength(), pathMeasure.getLength()}, value * pathMeasure.getLength()); mPaint.setPathEffect(effect); invalidate(); }});return mAnimator;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(250, 250);
        canvas.drawPath(mPath, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                createAnimator().start();
                break;
            case MotionEvent.ACTION_UP:
        }
        returnsuper.onTouchEvent(event); } @param R @param R @param R @param R @param R @param R @param R @param R @param R @param R @param R @param R @param RreturnPublic static Path nStarPath(Path Path, int num,float R, float r) {
        float perDeg = 360 / num;
        float degA = perDeg / 2 / 2;
        float degB = 360 / (num - 1) / 2 - degA / 2 + degA;
        path.moveTo((float) (Math.cos(rad(degA)) * R), (float) (-Math.sin(rad(degA)) * R));
        for (int i = 0; i < num; i++) {
            path.lineTo(
                    (float) (Math.cos(rad(degA + perDeg * i)) * R),
                    (float) (-Math.sin(rad(degA + perDeg * i)) * R));
            path.lineTo(
                    (float) (Math.cos(rad(degB + perDeg * i)) * r),
                    (float) (-Math.sin(rad(degB + perDeg * i)) * r));
        }
        path.close();
        returnpath; } /** * convert Angle to radians ** @param deg Angle * @return*/ public staticfloat rad(float deg) {
        return (float) (deg * Math.PI / 180); }}Copy the code

Section two: son of ValueAnimatorObjectAnimator and TimeAnimator:

ObjectAnimator animates “Xxx” properties that have a setXxx method. The ObjectAnimator animates “Xxx” properties that have a setXxx method. The ObjectAnimator animates “Xxx” properties that have a setXxx method


1. Test the built-in properties of View

1. Getting Started — Down example:

private ObjectAnimator mMoveDown; // Move the animation downCopy the code
MMoveDown = ObjectAnimator// Create an instance //(View, property name, initialization value, end value).offloat (this,"translationY", 0, 300) .setDuration(1000); // Set frequentlyCopy the code
@override // protected void onDraw(Canvas) {super.ondraw (Canvas); canvas.drawCircle(50, 50, 50, mPaint); }Copy the code
mMoveDown.start(); // Start the animationCopy the code

If you put that in the background, you can see that the whole View has changed.


2. Common attributes:
The property name demo explain
alpha
Transparency 1 to 0
translationX
It’s moving in the X direction
translationY
Y direction
rotation
Rotation (default View center point)
rotationX
X axis rotation (default View center horizontal axis)
rotationY
Y axis rotation (default View center vertical axis)
scaleX
X scale
scaleY
Y scale

3. Setting the center point of rotation and zooming:
setPivotX(200);
setPivotY(200);
Copy the code


4. Multiple parameters (The multi-parameter case applies to the Animator family)

360 360-0 – > – > 0 0 – – > 90

.ofFloat(this, "rotation", 0, 360360,0,0,90)Copy the code


Custom ObjectAnimator property

The built-in properties are just the usual ones, but we can also customize our own

1. Customize the circle size animation

The redraw method must be called with a setXxx method with the property name XXX

public void setRadius(int radius) { mRadius = radius; invalidate(); // Remember to redraw}Copy the code
ObjectAnimator// create an instance //(View, property name, initialization value, end value).fint (this,"Radius", 100, 50100,20,100). SetDuration (3000); // Set frequentlyCopy the code


2. Custom color animations
public void setColor(int color) { mColor = color; mPaint.setColor(mColor); invalidate(); // Remember to redraw}Copy the code
ColorAnimator = ObjectAnimator// Create instance //(View, property name, initialization value, end value).ofint (this,"color", 0xff0000ff, 0xffF2BA38, 0xffDD70BC) .setDuration(3000); colorAnimator.setEvaluator(new ArgbEvaluator()); // Color estimatorCopy the code


3. What is the difference between ValueAnimator and ObjectAnimator?
1.ValueAnimator needs to manually add listening, manually obtain ValueAnimator data, manually write the change logic 2.ObjectAnimator can not be updated listening, the core in 'setXxx 'in, that is, each update will go by itselfset3. The flexibility of ValueAnimator is better, after all, you can do it yourself, you can imagine how to play it. 4set5. Generally speaking, ObjectAnimator is oriented to application (concise and fast), ValueAnimator is oriented to operation (flexible and changeable).Copy the code

Third, TimeAnimator

This class is 100 lines of code, and almost half of it is comments. It inherits from ValueAnimator and is the apple of the Animator family, but it is very pure and focused and wants to do only one thing: provide a time stream (each 16 or 17ms callback method).

mAnimator = new TimeAnimator(); / / / / (himself, the total length, interval between each callback). MAnimator setTimeListener ((animation, totalTime, deltaTime) - > {the e (the TAG,"totalTime:" + totalTime + ", deltaTime:" + deltaTime);
    if(totalTime > 300) { animation.pause(); }});Copy the code

Running result:

TimeAnimatorView: totalTime:0, deltaTime:0 TotalTime :2, deltaTime:2 TimeAnimatorView: totalTime:19, deltaTime:17 TimeAnimatorView: totalTime:36, deltaTime:17 TotalTime :52, deltaTime:16 2018-12-27 10:09:35.118 E/TimeAnimatorView: TimeAnimatorView: totalTime:69, deltaTime:17 TotalTime :86, deltaTime:17 2018-12-27 10:09:35.151 E/TimeAnimatorView: TotalTime :102, deltaTime:16 2018-12-27 10:09:35.167 E/TimeAnimatorView: TotalTime :119, deltaTime:17 2018-12-27 10:09:35.184 E/TimeAnimatorView: TotalTime :136, deltaTime:17 2018-12-27 10:09:35.200e /TimeAnimatorView: TotalTime :152, deltaTime:16 2018-12-27 10:09:35.218 E/TimeAnimatorView: TimeAnimatorView: totalTime: 169e, deltaTime:17 TotalTime :186, deltaTime:17 2018-12-27 10:09:35.251 E/TimeAnimatorView: TotalTime :202, deltaTime:16 2018-12-27 10:09:358.268 E/TimeAnimatorView: TotalTime :219, deltaTime:17 2018-12-27 10:09:35.284e /TimeAnimatorView: TimeAnimatorView: totalTime: 236e /TimeAnimatorView: TotalTime :252, deltaTime:16 2018-12-27 10:09:35.318 E/TimeAnimatorView: TotalTime :269, deltaTime:17 2018-12-27 10:09:35.334 E/TimeAnimatorView: TimeAnimatorView: totalTime:303, deltaTime:17Copy the code

So that’s basically the end of the ValueAnimator story (there are a few more listeners, and finally together)


Fourth, the AnimatorSet

The AnimatorSet itself is not difficult to assemble from the previous animations

1. AnimatorSet in Builder mode

Each animation is a Node in the AnimatorSet. To process the relationship between the current Node and the inserted Node, look at the following set of animations:

MSet --> translationX --> translationX. Play (translationX)// translationX. With (alpha)// Tance.after (radiusAnimator)// radiusAnimator. / / discolorationCopy the code

Test source code:

public class AnimatorSetView extends View {
    private static final String TAG = "AnimatorView";
    private Paint mPaint;
    private int mRadius = 50;
    private int mColor = 50;
    private ObjectAnimator colorAnimator;
    private ObjectAnimator radiusAnimator;
    ObjectAnimator translationX;
    ObjectAnimator alpha;
    private AnimatorSet mSet;

    public AnimatorSetView(Context context) {
        this(context, null);
    }

    public AnimatorSetView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(0xff94E1F7); mSet = new AnimatorSet(); TranslationX = perspectiveAnimator translationX = perspectiveAnimator translationX = perspectiveAnimator translationX = PerspectiveAnimator translationX = PerspectiveAnimator translationX = PerspectiveAnimator translationX = PerspectiveAnimator translationX = PerspectiveAnimator translationX = PerspectiveAnimator"translationX", 0, 300, 150, 100, 20, 100) .setDuration(3000); // set alpha = ObjectAnimator// create an instance //(View, property name, initialization value, end value).offloat (this,"alpha", 1, 0.5f, 1, 0, 1).setduration (3000); // radiusAnimator = ObjectAnimator// Create an instance //(View, property name, initialization value, end value).fint (this,"Radius", 50, 100, 50, 100, 20, 100) .setDuration(3000); ColorAnimator = ObjectAnimator// Create an instance //(View, property name, initialization value, end value).fint (this,"color", 0xff0000ff, 0xffF2BA38, 0xffDD70BC) .setDuration(3000); colorAnimator.setEvaluator(new ArgbEvaluator()); // Radius --> translationX --> translationX. With (alpha).after(radiusAnimator).before(colorAnimator); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mRadius, mRadius, mRadius, mPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {case MotionEvent.ACTION_DOWN:
                mSet.start();
                break;
            case MotionEvent.ACTION_UP:
        }
        return super.onTouchEvent(event);
    }

    public void setRadius(int radius) {
        mRadius = radius;
        setMeasuredDimension(mRadius * 2, mRadius * 2); invalidate(); // Remember to redraw} public voidsetColor(int color) { mColor = color; mPaint.setColor(mColor); invalidate(); // Remember to redraw}}Copy the code

2.AnimatorSet itself method:

Just as the name suggests: exercise together or in batches

mSet.playTogether(translationX,alpha,radiusAnimator,colorAnimator);
mSet.playSequentially(translationX,alpha,radiusAnimator,colorAnimator);
Copy the code

Four, the monitoring of Animator:

An Animator has two internal interfaces, AnimatorListener and AnimatorPauseListener. AnimatorListenerAdapter is an empty implementation class for two interfaces, the standard adapter pattern. ValueAnimator as a child has its own interface, AnimatorUpdateListener

1.AnimatorListener: Animation monitor

The listener in the Animator can also be used by both children

Void onAnimationStart(Animator animation); Void onAnimationEnd(Animator animation); Void onAnimationCancel(Animator animation); Void onAnimationRepeat(Animator animation);Copy the code

2. Animation test

Start with green –> Repeat with random color –> Cancel with size to 50–> End with blue

mTranslationX = translationX();
mTranslationX.setRepeatMode(ValueAnimator.REVERSE);
mTranslationX.setRepeatCount(ValueAnimator.INFINITE);

mTranslationX.addListener(new Animator.AnimatorListener() {@override public void onAnimationStart(Animator animation) {// Start with greensetColor(Color.GREEN); } @override public void onAnimationEnd(Animator animation) {// Set the end to bluesetColor(Color.BLUE); } @override public void onAnimationCancel(Animator animation) {// Cancel when the size is 50setCircleR(50); } @override public void onAnimationRepeat(Animator animation) {// Set to random color when repeatingsetColor(ColUtils.randomColor()); }}); mTranslationX.start();Copy the code
mTranslationX.cancel(); // Cancel the animationCopy the code

3,AnimatorPauseListener: Animation pause listening
Void onAnimationPause(Animator animation); Void onAnimationResume(Animator animation);Copy the code

The effect is as follows: Click on motion, swipe right to pause the color and turn yellow, and slide down to restore the color and turn blue

mTranslationX.addPauseListener(new Animator.AnimatorPauseListener() {
    @Override
    public void onAnimationPause(Animator animation) {
        setColor(Color.YELLOW); } @override public void onAnimationResume(Animator animation) {setColor(Color.BLUE); // Restore blue}});Copy the code

4,AnimatorUpdateListener: ValueAnimator A series of proprietary monitors
Void onAnimationUpdate(ValueAnimator Animation);Copy the code

The effect is as follows: Each update is to link the radius and displacement

mTranslationX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mCircleR = (Float) animation.getAnimatedValue();
        invalidate();
    }
});
Copy the code

Use of the Animator family in XML:

Create the: animator folder under res

1. The Animator tag

Using the animator tag directly also feels a bit cumbersome, so take a look here

In the XML attribute meaning In the code
duration Duration of play setDuration()
valueType Parameter Value Type ofXXX
valueFrom The initial value OfXXX (para. 1)
valueTo The end value OfXXX (para. 2)
startOffset Time delay startDelay()
repeatCount Repeat the number setRepeatCount()
interpolator interpolator setRepeatMode()

1.1. The animator. XML

<? xml version="1.0" encoding="utf-8"? > <animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:repeatCount="2"
    android:repeatMode="reverse"
    android:startOffset="1000"
    android:valueFrom="0dp"
    android:valueType="floatType"
    android:valueTo="200dp">
</animator>
Copy the code

1.2. The layout
<? xml version="1.0" encoding="utf-8"? > <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <View
        android:id="@+id/id_btn_go"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="32dp"
        android:background="#3ED7FA"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
Copy the code

1.3. Use:MainActivity

Get ValueAnimator from Xml, and then do it yourself, which feels a bit cumbersome

View button = findViewById(R.id.id_btn_go);
ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator);

animator.addUpdateListener(anim->{
    float animatedValue = (float) anim.getAnimatedValue();
    button.setTranslationX(animatedValue);
});

button.setOnClickListener((v)->{
    animator.start();
});
Copy the code

2.setwithobjectAnimatorThe label

ObjectAnimator has one more propertyName property. The rest are consistent


2.1set_obj_animator.xml
<? xml version="1.0" encoding="utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:ordering="sequentially">
    <objectAnimator
        android:duration="1500"
        android:propertyName="rotationY"
        android:valueFrom="0"
        android:valueTo="180"/>
    <objectAnimator
        android:duration="1500"
        android:propertyName="alpha"
        android:valueFrom="0.3 f"
        android:valueTo="1f"/>
    <objectAnimator
        android:duration="1500"
        android:propertyName="translationX"
        android:valueFrom="0"
        android:valueTo="180dp"/>
</set>
Copy the code

2.2: used in code
View button = findViewById(R.id.id_btn_go);
Animator set_obj = AnimatorInflater.loadAnimator(this, R.animator.set_obj_animator);
et_obj.setTarget(button);
        
button.setOnClickListener((v)->{
    set_obj.start();
});
Copy the code

3, take a last look at me bigobjectAnimatorTransformation path

The complete guide to Vector maps in Android resource Res (with analysis of the SVG-path command)

Arrows: M8,50, L100,0 M0,47, L40,40 M0,52 L40-40 Menu: M0,50, L80,0 M0,80, L80,0 M0,20 L80 0Copy the code
The path of deformation Deformation + rotation

1. Put the two path strings into string.xml

You can write directly, but it’s not easy to reuse

<resources>
    <string name="app_name">test</string>
    <string name="path_from"</string> <string name="path_to"</string> </resources> </resources>Copy the code

2. Vector diagram: path_test.xml
<? xml version="1.0" encoding="utf-8"? > <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="100"
        android:viewportHeight="100">
    <group
        android:translateX="4"
        android:translateY="4">
        <path
            android:pathData="M0, A30 0, 50,90,0,1,50,50"
            android:strokeWidth="4"
            android:strokeColor="@color/black"/>
    </group>
</vector>
Copy the code

3. Rotate animation: rotation_animator.xml
<? xml version="1.0" encoding="utf-8"? > <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="180"/>

Copy the code

4. Path animation: path_animator.xml
<? xml version="1.0" encoding="utf-8"? > <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/linear"
    android:propertyName="pathData"
    android:valueFrom="@string/path_from"
    android:valueTo="@string/path_to"
    android:valueType="pathType"/>
Copy the code

5. Vector drawing file:icon_path.xml
<? xml version="1.0" encoding="utf-8"? > <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="100"
        android:viewportHeight="100">
    <group android:name="container"
        android:translateX="8"
        android:pivotX="50"
           android:scaleY="0.8"
           android:scaleX="0.8"
        android:pivotY="50">

        <path
            android:name="alpha_anim"
            android:pathData="@string/path_from"
            android:strokeWidth="8"
            android:strokeColor="# 000"/>
    </group>
</vector>
Copy the code

6. Integrate animation: anima_path.xml
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/icon_path">
    <target
        android:name="alpha_anim"
        android:animation="@animator/path_animator"/>
    <target
        android:name="container"
        android:animation="@animator/rotation_animator">
    </target>
</animated-vector>
Copy the code

7. Use animations:
 <ImageView
     android:id="@+id/id_iv"
     android:layout_width="200dp"
     android:layout_height="200dp"
     android:src="@drawable/anima_path"
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintEnd_toEndOf="parent"
     app:layout_constraintStart_toStartOf="parent"
     app:layout_constraintTop_toTopOf="parent"/>
Copy the code
Drawable Drawable = midiv.getDrawable (); Drawable Drawable = midiv.getDrawable ();if (drawable instanceof Animatable){
    ((Animatable) drawable).start();
}
Copy the code

Ok, that’s it, you can customize both paths as much as you want, but you have to make sure that the instructions for both paths are the same, otherwise it will crash


Postscript: Jie wen standard

1. Growth record and Errata of this paper
Program source code The date of note
V0.1 – making 2018-12-27 Android Animator family Guide
2. More about me
Pen name QQ WeChat hobby
Zhang Feng Jie te Li 1981462002 zdl1994328 language
My lot My Jane books I’m the nuggets Personal website
3. The statement

1—- this article is originally written by Zhang Feng Jetelie, please note 2—- all programming enthusiasts are welcome to communicate with each other 3—- personal ability is limited, if there is any error, you are welcome to criticize and point out, will be modest to correct 4—- See here, I thank you here for your love and support