Android 动画总结(4) - 插值器
2018-04-01 本文已影响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:tension
和 android: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:pathData
,android:controlX1
,android:controlY1
,android:controlX2
,android: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" />