Android animation and Overview covers the following:

  • Animation and transition (I), view animation overview and use
  • Animation and Transition (2). View Animation: Define and extend Animation
  • Animation and transition (III), interpolators and estimators overview and use
  • Animation and transition (4), using Layout, offset, layoutParam to achieve displacement animation
  • Animation and transition (five), using scrollTo, scrollBy, Scroller to achieve rolling animation
  • Animation and transition (6), using ViewDragHelper to achieve smooth drag animation
  • Add an entry animation to a ViewGroup and a LayoutAnimation
  • Animation and transition (8), for Viewgroup to provide delete, add smooth animation effect, LayoutTransition use overview
  • Animation and transition (nine), Svg animation use overview, Vector Drawable use, three-party SVGA framework use
  • Introduction to the use of Property Animation
  • Use FlingAnimation to move a view and create a motion picture
  • Add animation to the view using the physical SpringAnimation. Use the overview of SpringAnimation
  • Animation and Transition (13), View, Activity Transition transition animation use overview
  • Animation and transition (XIV), Overview of Lottie Animation use
  • Animation and transition combat (15), like toutiao column drag sorting effect
  • Animation and transition combat (16), imitation IOS sideslip delete effect
  • Animation and transition actual combat (17), imitation explore card flip effect

When it comes to animation, there are a lot of things you can’t get around, such as Matrix, and of course interpolators and estimators

1. Interpolators

Interpolators will interpolate at Interpolator. Interpolator will Interpolator at Interpolator. Interpolator is interpolated at ViewAnimation, ObjectAnimator, ValueAnimator. Interpolators are used to control the rate of change in animation execution. You could also call it an accelerator.

1-1 common interpolators in Android development

The diagram above is an interpolator implementation class defined by the Android system.

Interpolators all interpolators implement Interpolator interface, which Interpolator interface inherits from TimeInterpolator.

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.
     *
     * @paramInput A value between 0 and 1.0 indicating that it was our current point * in the animation where 0 indicates the start and 1.0 represents * the end *@return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}
Copy the code

The TimeInterpolator interface has only one method getInterpolation(float Input); , this method returns a change from 0 to 1 during scheduling time. Therefore, we change the different animation interpolators, in fact, we use different curve algorithms in this method, and constantly return the change value of a curve time point that is close to the physical law.

The following is a brief introduction to the interpolators provided by the system:

The 1-1-1,AccelerateDecelerateInterpolatorAcceleration first, deceleration later effect interpolator

  • The core code
public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolator {...// omit some methods
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0 f) + 0.5 f; }}Copy the code

The 1-1-2,LinearInterpolatorConstant velocity interpolator

Later due to space reasons and GIF production is time-consuming, so only for the more difficult to understand the set of new GIF

  • Core code:
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {...// omit some methods
	// Return as many inputs as possible at constant speed
    public float getInterpolation(float input) {
        returninput; }}Copy the code

The 1-1-3,DecelerateInterpolatorRetarding interpolator

  • Core code:
public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolator {...// omit some methods
    public float getInterpolation(float input) {
        float result;
        if (mFactor == 1.0 f) {
            result = (float) (1.0 f - (1.0 f - input) * (1.0 f - input));
        } else {
            result = (float) (1.0 f - Math.pow((1.0 f - input), 2 * mFactor));
        }
        return result;
    }
    private float mFactor = 1.0 f;
}
Copy the code

The 1-1-3,AccelerateInterpolatorAccelerated interpolator

  • Core code:
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolator {...// omit some methods
    public AccelerateInterpolator(float factor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
    }
    
    public float getInterpolation(float input) {
        if (mFactor == 1.0 f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor); }}}Copy the code

The 1-1-4,AnticipateInterpolatorFirst along the reverse movement for a distance, then accelerate along the positive direction, similar to playing badminton when the swing effect, first back swing, then accelerate hit.

  • Core code:
public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolator {
    private final floatmTension; .// omit some methods
    public AnticipateInterpolator(a) {
        mTension = 2.0 f;
    }
    public float getInterpolation(float t) {
        // a(t) = t * t * ((tension + 1) * t - tension)
        return t * t * ((mTension + 1) * t - mTension); }}Copy the code

The 1-1-5,AnticipateOvershootInterpolatorIt moves in the opposite direction for a certain distance, then accelerates, then slows down and passes the critical point for a certain distance.

  • Core code:
public class AnticipateOvershootInterpolator extends BaseInterpolator
        implements NativeInterpolator {
    private final floatmTension; .// omit some methods
    public AnticipateOvershootInterpolator(a) {
        mTension = 2.0 f * 1.5 f;
    }

    / * * *@paramTension Amount of anticipation/overshoot. When tension equals 0.0f, * there is no anticipation/overshoot and the interpolator becomes * a simple acceleration/deceleration interpolator. */
    public AnticipateOvershootInterpolator(float tension) {
        mTension = tension * 1.5 f;
    }

    private static float a(float t, float s) {
        return t * t * ((s + 1) * t - s);
    }

    private static float o(float t, float s) {
        return t * t * ((s + 1) * t + s);
    }

    public float getInterpolation(float t) {
        // a(t, s) = t * t * ((s + 1) * t - s)
        // o(t, s) = t * t * ((s + 1) * t + s)
        // f(t) = 0.5 * a(t * 2, tension * extraTension), when t < 0.5
        // f(t) = 0.5 * (o(t * 2-2, tension * extraTension) + 2), when t <= 1.0
        if (t < 0.5 f) return 0.5 f * a(t * 2.0 f, mTension);
        else return 0.5 f * (o(t * 2.0 f - 2.0 f, mTension) + 2.0 f); }}Copy the code

The 1-1-6,BounceInterpolatorWhen it reaches the critical value, it will perform attenuation rebound effect, similar to the effect of a ball falling from high

  • Core code:
public class BounceInterpolator extends BaseInterpolator implements NativeInterpolator {
    public BounceInterpolator(a) {}...// omit some methods
    @SuppressWarnings({"UnusedDeclaration"})
    public BounceInterpolator(Context context, AttributeSet attrs) {}private static float bounce(float t) {
        return t * t * 8.0 f;
    }

    public float getInterpolation(float t) {
        // _b(t) = t * t * 8
        // bs(t) = _b(t) for t < 0.3535
        // bs(t) = _b(t-0.54719) + 0.7 for t < 0.7408
        // bs(t) = _b(t-0.8526) + 0.9 for t < 0.9644
        // bs(t) = _b(t-1.0435) + 0.95 for t <= 1.0
        // b(t) = bs(t * 1.1226)
        t *= 1.1226 f;
        if (t < 0.3535 f) return bounce(t);
        else if (t < 0.7408 f) return bounce(t - 0.54719 f) + 0.7 f;
        else if (t < 0.9644 f) return bounce(t - 0.8526 f) + 0.9 f;
        else return bounce(t - 1.0435 f) + 0.95 f; }}Copy the code

The 1-1-7,CycleInterpolatorIt’s moving along a sinusoidal trajectory

  • Core code:
public class CycleInterpolator extends BaseInterpolator implements NativeInterpolator {
    public CycleInterpolator(float cycles) { mCycles = cycles; }...// omit some methods
    public float getInterpolation(float input) {
        return (float)(Math.sin(2 * mCycles * Math.PI * input));
    }

    private float mCycles;
}
Copy the code

The 1-1-8,OvershootInterpolatorSpeed up for a while, then slow down, and move beyond the critical point for a while.

  • Core code:
public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolator {
    private final float mTension;

    public OvershootInterpolator(a) {
        mTension = 2.0 f; }...// omit some methods
    / * * *@paramTension Amount of overshoot. When tension equals 0.0f, there is * no overshoot and the interpolator becomes a simple * deceleration interpolator. */
    public OvershootInterpolator(float tension) {
        mTension = tension;
    }

    public float getInterpolation(float t) {
        // _o(t) = t * t * ((tension + 1) * t + tension)
        // o(t) = _o(t - 1) + 1
        t -= 1.0 f;
        return t * t * ((mTension + 1) * t + mTension) + 1.0 f; }}Copy the code

The 1-1-9,PathInterpolatorUniversal interpolator, you can directly pass in a path, according to the path path run

  • Core code:
public class PathInterpolator extends BaseInterpolator implements NativeInterpolator {...// omit some methods
    // This governs how accurate the approximation of the Path is.
    private static final float PRECISION = 0.002 f;

    private float[] mX; // x coordinates in the line

    private float[] mY; // y coordinates in the line

    /**
     * Create an interpolator for an arbitrary <code>Path</code>. The <code>Path</code>
     * must begin at <code>(0, 0)</code> and end at <code>(1, 1)</code>.
     *
     * @param path The <code>Path</code> to use to make the line representing the interpolator.
     */
    public PathInterpolator(Path path) {
        initPath(path);
    }

    /**
     * Create an interpolator for a quadratic Bezier curve. The end points
     * <code>(0, 0)</code> and <code>(1, 1)</code> are assumed.
     *
     * @param controlX The x coordinate of the quadratic Bezier control point.
     * @param controlY The y coordinate of the quadratic Bezier control point.
     */
    public PathInterpolator(float controlX, float controlY) {
        initQuad(controlX, controlY);
    }

    /**
     * Create an interpolator for a cubic Bezier curve.  The end points
     * <code>(0, 0)</code> and <code>(1, 1)</code> are assumed.
     *
     * @param controlX1 The x coordinate of the first control point of the cubic Bezier.
     * @param controlY1 The y coordinate of the first control point of the cubic Bezier.
     * @param controlX2 The x coordinate of the second control point of the cubic Bezier.
     * @param controlY2 The y coordinate of the second control point of the cubic Bezier.
     */
    public PathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) {
        initCubic(controlX1, controlY1, controlX2, controlY2);
    }

    public PathInterpolator(Context context, AttributeSet attrs) {
        this(context.getResources(), context.getTheme(), attrs);
    }

    / * *@hide* /
    public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;
        if(theme ! =null) {
            a = theme.obtainStyledAttributes(attrs, R.styleable.PathInterpolator, 0.0);
        } else {
            a = res.obtainAttributes(attrs, R.styleable.PathInterpolator);
        }
        parseInterpolatorFromTypeArray(a);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    private void parseInterpolatorFromTypeArray(TypedArray a) {
        // If there is pathData defined in the xml file, then the controls points
        // will be all coming from pathData.
        if (a.hasValue(R.styleable.PathInterpolator_pathData)) {
            String pathData = a.getString(R.styleable.PathInterpolator_pathData);
            Path path = PathParser.createPathFromPathData(pathData);
            if (path == null) {
                throw new InflateException("The path is null, which is created"
                        + " from " + pathData);
            }
            initPath(path);
        } else {
            if(! a.hasValue(R.styleable.PathInterpolator_controlX1)) {throw new InflateException("pathInterpolator requires the controlX1 attribute");
            } else if(! a.hasValue(R.styleable.PathInterpolator_controlY1)) {throw new InflateException("pathInterpolator requires the controlY1 attribute");
            }
            float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0);
            float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0);

            boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2);
            boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2);

            if(hasX2 ! = hasY2) {throw new InflateException(
                        "pathInterpolator requires both controlX2 and controlY2 for cubic Beziers.");
            }

            if(! hasX2) { initQuad(x1, y1); }else {
                float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0);
                float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0); initCubic(x1, y1, x2, y2); }}}private void initQuad(float controlX, float controlY) {
        Path path = new Path();
        path.moveTo(0.0);
        path.quadTo(controlX, controlY, 1f.1f);
        initPath(path);
    }

    private void initCubic(float x1, float y1, float x2, float y2) {
        Path path = new Path();
        path.moveTo(0.0);
        path.cubicTo(x1, y1, x2, y2, 1f.1f);
        initPath(path);
    }

    private void initPath(Path path) {
        float[] pointComponents = path.approximate(PRECISION);

        int numPoints = pointComponents.length / 3;
        if (pointComponents[1] != 0 || pointComponents[2] != 0
                || pointComponents[pointComponents.length - 2] != 1
                || pointComponents[pointComponents.length - 1] != 1) {
            throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)");
        }

        mX = new float[numPoints];
        mY = new float[numPoints];
        float prevX = 0;
        float prevFraction = 0;
        int componentIndex = 0;
        for (int i = 0; i < numPoints; i++) {
            float fraction = pointComponents[componentIndex++];
            float x = pointComponents[componentIndex++];
            float y = pointComponents[componentIndex++];
            if(fraction == prevFraction && x ! = prevX) {throw new IllegalArgumentException(
                        "The Path cannot have discontinuity in the X axis.");
            }
            if (x < prevX) {
                throw new IllegalArgumentException("The Path cannot loop back on itself."); } mX[i] = x; mY[i] = y; prevX = x; prevFraction = fraction; }}/**
     * Using the line in the Path in this interpolator that can be described as
     * <code>y = f(x)</code>, finds the y coordinate of the line given <code>t</code>
     * as the x coordinate. Values less than 0 will always return 0 and values greater
     * than 1 will always return 1.
     *
     * @param t Treated as the x coordinate along the line.
     * @return The y coordinate of the Path along the line where x = <code>t</code>.
     * @see Interpolator#getInterpolation(float)
     */
    @Override
    public float getInterpolation(float t) {
        if (t <= 0) {
            return 0;
        } else if (t >= 1) {
            return 1;
        }
        // Do a binary search for the correct x to interpolate between.
        int startIndex = 0;
        int endIndex = mX.length - 1;

        while (endIndex - startIndex > 1) {
            int midIndex = (startIndex + endIndex) / 2;
            if (t < mX[midIndex]) {
                endIndex = midIndex;
            } else{ startIndex = midIndex; }}float xRange = mX[endIndex] - mX[startIndex];
        if (xRange == 0) {
            return mY[startIndex];
        }

        float tInRange = t - mX[startIndex];
        float fraction = tInRange / xRange;

        float startY = mY[startIndex];
        float endY = mY[endIndex];
        returnstartY + (fraction * (endY - startY)); }}Copy the code

The interpolator is simple to use.

Used in Java files

android:interpolator="@android:anim/accelerate_decelerate_interpolator"
Copy the code

XML file

scaleAnimation.interpolator = LinearInterpolator()
Copy the code

1-2. Custom interpolators

Next we implement a curve as followsThe following is the implementation code, from the above system implementation, basically can know that the core is to achieveInterpolatorInterface. ingetInterpolation(input: Float): FloatIn writing calculation algorithm.

In fact, in the diagram above, the algorithm is already listed, so it just needs to be written to this method.

class EaseInElasticInterpolator: Interpolator {
    override fun getInterpolation(input: Float): Float {
        val c4 = (2 * Math.PI) / 3
        return when (input) {
            0F- > {0F
            }
            1F- > {1F
            }
            else -> {
                ((-2.0).pow((10 * input - 10).toDouble()) * sin((input * 10F - 10.75 F) * c4)).toFloat()
            }
        }
    }
}
Copy the code

Introduce a few very easy to use auxiliary websites, can help us achieve twice the result with half the effort.

  • Interpolator online view effect site

  • Curve effect online demo site

  • Bessel curve on-line debugging

  • Easings is an open source interpolation algorithm site, covering a wide variety of interpolation algorithms, most of the physical scene can be found.
https://github.com/ai/easings.net
Copy the code