Android 動(dòng)畫(huà)總結(jié)(1) - 概述
Android 動(dòng)畫(huà)總結(jié)(2) - 幀動(dòng)畫(huà)
Android 動(dòng)畫(huà)總結(jié)(3) - 補(bǔ)間動(dòng)畫(huà)
Android 動(dòng)畫(huà)總結(jié)(5) - 屬性動(dòng)畫(huà)
Android 動(dòng)畫(huà)總結(jié)(6) - 估值器
Android 動(dòng)畫(huà)總結(jié)(7) - ViewGroup 子元素間的動(dòng)畫(huà)
Android 動(dòng)畫(huà)總結(jié)(8) - Activity 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
Android 動(dòng)畫(huà)總結(jié)(9) - 過(guò)渡動(dòng)畫(huà)
Interpolator 插值器继低,作用就是把 0 到 1 的浮點(diǎn)值變化映射到另一個(gè)浮點(diǎn)值變化熬苍,即根據(jù)時(shí)間流逝百分比計(jì)算出動(dòng)畫(huà)變化百分比。
圖片切線就是速度郁季。
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;
}
}
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);
}
}
}
一個(gè)屬性 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);
}
}
有一個(gè)屬性 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);
}
}
有兩個(gè)屬性 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;
}
}
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;
}
有一個(gè)屬性 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;
}
有一個(gè)屬性 android:factor
LinearInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public float getInterpolation(float input) {
return input;
}
}
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;
}
}
有一個(gè)屬性 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 構(gòu)建
* Path 開(kāi)始前必須是 (0,0)冷溃,結(jié)束時(shí)必須是 (1,1)
*/
public PathInterpolator(Path path) {
initPath(path);
}
/**
* 用 (x,y) 坐標(biāo)點(diǎn)構(gòu)建
* 起點(diǎn)是 (0,0),結(jié)束是 (1,1)梦裂,參數(shù)是貝塞爾曲線的控制點(diǎn)坐標(biāo)
*/
public PathInterpolator(float controlX, float controlY) {
initQuad(controlX, controlY);
}
/**
* 貝塞爾曲線的兩個(gè)控制點(diǎn)
*/
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 路徑就完全用這個(gè)
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 {
// 說(shuō)明沒(méi)有定義 pathData 時(shí)必須定義 controlX1 和 controlY1 這一對(duì)控制點(diǎn)以繪制貝塞爾曲線
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 要么同時(shí)有年柠,要么同時(shí)沒(méi)有凿歼。多加一個(gè)控制點(diǎn)
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 繪制曲線確定的函數(shù) <code>y = f(x)</code>,速度就按這個(gè)變化
*/
@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));
}
}
有五個(gè)屬性 android:pathData
冗恨,android:controlX1
答憔,android:controlY1
,android:controlX2
掀抹,android:controlY2
虐拓。
Support V4 下的兼容插值器
LookupTableInterpolator 是一個(gè)抽象類,子類要傳入一個(gè) float 數(shù)組傲武,根據(jù)傳入的 input 返回蓉驹,這個(gè)值就是用數(shù)組里已經(jīng)定義好的數(shù)字按一定的算法返回。
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]);
}
}
三個(gè)繼承者揪利,區(qū)別在于 float 數(shù)組的值不同:
- FastOutLinearInInterpolator
- FastOutSlowInInterpolator
- LinearOutSlowInInterpolator
自定義
res/anim
目錄下創(chuàng)建 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" />