美文网首页
Android 动画总结(4) - 插值器

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

作者: 三流之路 | 来源:发表于2018-04-01 18:25 被阅读0次

    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;
        }
    
    }
    
    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);
            }
        }
    
    }
    
    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);
        }
    
    }
    
    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);
        }
    
    }
    
    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;
        }
    
    }
    
    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;
    }
    
    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;
    
    }
    
    DecelerateInterpolator.png

    有一个属性 android:factor

    LinearInterpolator

    public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    
        public float getInterpolation(float input) {
            return input;
        }
    
    }
    
    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;
        }
    
    }
    
    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 绘制曲线确定的函数 <code>y = f(x)</code>,速度就按这个变化
         */
        @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,修改原生插值器的属性值:

    <?xml version="1.0" encoding="utf-8"?>
    <overshootInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
        android:tension="7.0" />
    

    然后使用自定义的插值器

    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@anim/my_overshoot_interpolator"
        android:fromXScale="1.0"
        android:toXScale="3.0"
        android:fromYScale="1.0"
        android:toYScale="3.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="700" />
    

    相关文章

      网友评论

          本文标题:Android 动画总结(4) - 插值器

          本文链接:https://www.haomeiwen.com/subject/mkjacftx.html