前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 动画总结(4) - 插值器

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

作者头像
三流编程
发布2018-09-11 16:18:22
1K0
发布2018-09-11 16:18:22
举报

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

图片切线就是速度。

AccelerateDecelerateInterpolator

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

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

}

LinearInterpolator.png

OvershootInterpolator

代码语言:javascript
复制
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

代码语言:javascript
复制
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 返回,这个值就是用数组里已经定义好的数字按一定的算法返回。

代码语言:javascript
复制
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,修改原生插值器的属性值:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<overshootInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:tension="7.0" />

然后使用自定义的插值器

代码语言:javascript
复制
<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" />
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.04.01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AccelerateDecelerateInterpolator
  • AccelerateInterpolator
  • AnticipateInterpolator
  • AnticipateOvershootInterpolator
  • BounceInterpolator
  • CycleInterpolator
  • DecelerateInterpolator
  • LinearInterpolator
  • OvershootInterpolator
  • PathInterpolator
  • Support V4 下的兼容插值器
  • 自定义
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档