Android 动画总结(4) - 插值器

Android 动画总结(1) - 概述
Android 动画总结(2) - 帧动画
Android 动画总结(3) - 补间动画
Android 动画总结(5) - 属性动画
Android 动画总结(6) - 估值器
Android 动画总结(7) - ViewGroup 子元素间的动画
Android 动画总结(8) - Activity 转场动画
Android 动画总结(9) - 过渡动画


Interpolator 插值器,作用就是把 0 到 1 的浮点值变化映射到另一个浮点值变化,即根据时间流逝百分比计算出动画变化百分比。

图片切线就是速度。

AccelerateDecelerateInterpolator

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

}
Android 动画总结(4) - 插值器_第1张图片
AccelerateDecelerateInterpolator.png

AccelerateInterpolator

public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    private final float mFactor;
    private final double mDoubleFactor;

    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }

    public AccelerateInterpolator(float factor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
    }

    public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;

        mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f);
        mDoubleFactor = 2 * mFactor;
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }

}
Android 动画总结(4) - 插值器_第2张图片
AccelerateInterpolator.png

一个属性 android:factor

AnticipateInterpolator

public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    private final float mTension;

    public AnticipateInterpolator() {
        mTension = 2.0f;
    }

    public AnticipateInterpolator(float tension) {
        mTension = tension;
    }

    public AnticipateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;

        mTension = a.getFloat(R.styleable.AnticipateInterpolator_tension, 2.0f);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    public float getInterpolation(float t) {
        // a(t) = t * t * ((tension + 1) * t - tension)
        return t * t * ((mTension + 1) * t - mTension);
    }

}
Android 动画总结(4) - 插值器_第3张图片
AnticipateInterpolator.png

有一个属性 android:tension

AnticipateOvershootInterpolator

public class AnticipateOvershootInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    private final float mTension;

    public AnticipateOvershootInterpolator() {
        mTension = 2.0f * 1.5f;
    }

    public AnticipateOvershootInterpolator(float tension) {
        mTension = tension * 1.5f;
    }

    public AnticipateOvershootInterpolator(float tension, float extraTension) {
        mTension = tension * extraTension;
    }

    public AnticipateOvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;

        mTension = a.getFloat(AnticipateOvershootInterpolator_tension, 2.0f) *
                a.getFloat(AnticipateOvershootInterpolator_extraTension, 1.5f);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    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.5f) return 0.5f * a(t * 2.0f, mTension);
        else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
    }

}
Android 动画总结(4) - 插值器_第4张图片
AnticipateOvershootInterpolator.png

有两个属性 android:tensionandroid:extraTension

BounceInterpolator

public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    private static float bounce(float t) {
        return t * t * 8.0f;
    }

    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.1226f;
        if (t < 0.3535f) return bounce(t);
        else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
        else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
        else return bounce(t - 1.0435f) + 0.95f;
    }

}
Android 动画总结(4) - 插值器_第5张图片
BounceInterpolator.png

CycleInterpolator

public class CycleInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public CycleInterpolator(Resources resources, Theme theme, AttributeSet attrs) {
        TypedArray a;

        mCycles = a.getFloat(R.styleable.CycleInterpolator_cycles, 1.0f);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    public float getInterpolation(float input) {
        return (float)(Math.sin(2 * mCycles * Math.PI * input));
    }

    private float mCycles;
}
Android 动画总结(4) - 插值器_第6张图片
CycleInterpolator.png

有一个属性 android:cycles

DecelerateInterpolator

public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public DecelerateInterpolator(float factor) {
        mFactor = factor;
    }

    public DecelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;

        mFactor = a.getFloat(R.styleable.DecelerateInterpolator_factor, 1.0f);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    public float getInterpolation(float input) {
        float result;
        if (mFactor == 1.0f) {
            result = (float)(1.0f - (1.0f - input) * (1.0f - input));
        } else {
            result = (float)(1.0f - Math.pow((1.0f - input), 2 * mFactor));
        }
        return result;
    }

    private float mFactor = 1.0f;

}
Android 动画总结(4) - 插值器_第7张图片
DecelerateInterpolator.png

有一个属性 android:factor

LinearInterpolator

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public float getInterpolation(float input) {
        return input;
    }

}
Android 动画总结(4) - 插值器_第8张图片
LinearInterpolator.png

OvershootInterpolator

public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    private final float mTension;

    public OvershootInterpolator() {
        mTension = 2.0f;
    }

    public OvershootInterpolator(float tension) {
        mTension = tension;
    }

    public OvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;

        mTension = a.getFloat(R.styleable.OvershootInterpolator_tension, 2.0f);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

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

}
Android 动画总结(4) - 插值器_第9张图片
OvershootInterpolator.png

有一个属性 android:tension

PathInterpolator

public class PathInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    // This governs how accurate the approximation of the Path is.
    private static final float PRECISION = 0.002f;

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

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

    /**
     * 用 Path 构建
     * Path 开始前必须是 (0,0),结束时必须是 (1,1)
     */
    public PathInterpolator(Path path) {
        initPath(path);
    }

    /**
     * 用 (x,y) 坐标点构建
     * 起点是 (0,0),结束是 (1,1),参数是贝塞尔曲线的控制点坐标
     */
    public PathInterpolator(float controlX, float controlY) {
        initQuad(controlX, controlY);
    }

    /**
     * 贝塞尔曲线的两个控制点
     */
    public PathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) {
        initCubic(controlX1, controlY1, controlX2, controlY2);
    }

    public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) {
        TypedArray a;

parseInterpolatorFromTypeArray(a);
        setChangingConfiguration(a.getChangingConfigurations());
        a.recycle();
    }

    private void parseInterpolatorFromTypeArray(TypedArray a) {
        // 如果 xml 定义了 pathData 属性,那么 Path 路径就完全用这个
        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 {
            // 说明没有定义 pathData 时必须定义 controlX1 和 controlY1 这一对控制点以绘制贝塞尔曲线
            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);
            // controlX2,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;
        }
    }

    /**
     * Path 绘制曲线确定的函数 y = f(x),速度就按这个变化
     */
    @Override
    public float getInterpolation(float t) {
        if (t <= 0) {
            return 0;
        } else if (t >= 1) {
            return 1;
        }
        // 二分查找
        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];
        return startY + (fraction * (endY - startY));
    }
}

有五个属性 android:pathDataandroid:controlX1android:controlY1android:controlX2android:controlY2

Support V4 下的兼容插值器

LookupTableInterpolator 是一个抽象类,子类要传入一个 float 数组,根据传入的 input 返回,这个值就是用数组里已经定义好的数字按一定的算法返回。

abstract class LookupTableInterpolator implements Interpolator {

    private final float[] mValues;
    private final float mStepSize;

    public LookupTableInterpolator(float[] values) {
        mValues = values;
        mStepSize = 1f / (mValues.length - 1);
    }

    @Override
    public float getInterpolation(float input) {
        if (input >= 1.0f) {
            return 1.0f;
        }
        if (input <= 0f) {
            return 0f;
        }

        int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2);

        float quantized = position * mStepSize;
        float diff = input - quantized;
        float weight = diff / mStepSize;

        return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
    }

}

三个继承者,区别在于 float 数组的值不同:

  • FastOutLinearInInterpolator
  • FastOutSlowInInterpolator
  • LinearOutSlowInInterpolator

自定义

res/anim 目录下创建 my_overshoot_interpolator.xml,修改原生插值器的属性值:



然后使用自定义的插值器


你可能感兴趣的:(Android 动画总结(4) - 插值器)