概述
前兩篇已經(jīng)講了屬性動畫的使用和源碼的實現(xiàn)。但是大家應(yīng)該發(fā)現(xiàn)了還有非常重要的一部分沒有提及,那就是插值器。無論是在屬性動畫還是在View動畫中,都有一個非常重要的類Interporator,這個類獲取到當(dāng)前的時間進度,然后根據(jù)時間進入計算得到我們想要的插值。通俗易懂的語言來說,這就是一個y=f(t)的函數(shù),輸入的是時間,輸出的是變換后的值。為了自己實現(xiàn)一個屬性動畫的效果,就先來看下這個重要的類的實現(xiàn),系統(tǒng)提供的默認(rèn)Interporator。
插值器
在動畫中應(yīng)用插值器非常簡單,在View動畫中只要設(shè)置setInterpolator就可以了。
outerScaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
在屬性動畫中也可以一樣簡單地設(shè)置:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "TranslateX", 0.f, 1.f);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
系統(tǒng)提供了蠻多插值器的,如線性插值器,先加速后減速插值器,加速插值器,減速插值器,末尾震蕩插值器等。

這些插值器都是從BaseInterpolator繼承而來,BaseInterpolator實現(xiàn)了Interpolator接口,Interpolator繼承自TimeInterpolator。在TimeInterpolator中,只有一個需要實現(xiàn)的方法。這個方法的意思就是說,我們需要輸入0-1.0的表示進度的數(shù),而輸出可已超過1.0的表示已經(jīng)經(jīng)由此方法轉(zhuǎn)換了的值。
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}
BaseInterpolator的代碼中,有兩個hide的方法,說明不是我們應(yīng)該調(diào)用的,就不去管它。然后看下簡單的幾種插值器的實現(xiàn)。
首先來看下最簡單的線性插值器,可以發(fā)現(xiàn)非常簡單。getInterpolation方法直接將輸入返回。這也非常好理解,既然是線性的,那么就是y=x的這種變換。
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
然后再來看下復(fù)雜點的AccelerateInterpolator 。仔細分析下,發(fā)現(xiàn)除了在XML布局設(shè)置需要的構(gòu)造器外,邏輯代碼非常簡單明了。只有一個方法起到了作用
@HasNativeInterpolator
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
}
/**
* Constructor
*
* @param factor Degree to which the animation should be eased. Seting
* factor to 1.0f produces a y=x^2 parabola. Increasing factor above
* 1.0f exaggerates the ease-in effect (i.e., it starts even
* slower and ends evens faster)
*/
public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}
public AccelerateInterpolator(Context context, AttributeSet attrs) {
this(context.getResources(), context.getTheme(), attrs);
}
/** @hide */
public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
TypedArray a;
if (theme != null) {
a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0);
} else {
a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator);
}
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);
}
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
}
}
看下這個方法,如果我們設(shè)置的mFactor等于1的話,那么就是y=x2這個形式的函數(shù)。如果不是,那么是y=xfactor的形式。到達終點前會加速趨近于1。
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
上面介紹了Interpolator的用法,但是這只是0-1.0的變換的插值。如何將這個值再次換算到最終顯示的值呢。其實在上篇中的源碼中也有,但是沒有詳細講。如下的代碼是animateValue方法,用處是將最后的結(jié)果通知調(diào)用者。fraction = mInterpolator.getInterpolation(fraction);這個前面已經(jīng)講到了。
@CallSuper
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
然后看下calculateValue這個方法,這個方法中只有兩行代碼,主要是第一行,getValue中可以通過mEvaluator(當(dāng)然首先要去設(shè)置)去獲取最終的值。
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
getValue是抽象的方法,需要子類去實現(xiàn)。setEvaluator也是Keyframes中的抽象方法,除了系統(tǒng)實現(xiàn)的幾個外(在PathKeyframes中),我們需要自己定義,然后用setEvaluator設(shè)置。
/**
* Sets the TypeEvaluator to be used when calculating animated values. This object
* is required only for Keyframes that are not either IntKeyframes or FloatKeyframes,
* both of which assume their own evaluator to speed up calculations with those primitive
* types.
*
* @param evaluator The TypeEvaluator to be used to calculate animated values.
*/
void setEvaluator(TypeEvaluator evaluator);
系統(tǒng)提供的默認(rèn)的Evaluator,有Int和float還有顏色屬性的Evaluator,也可以自己繼承TypeEvaluator去實現(xiàn)。

TypeEvaluator是一個接口,只有一個方法evaluate。我們只要繼承就可以了。
public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}
實現(xiàn)一個自定義的屬性動畫
我們想實現(xiàn)一個阻尼正震蕩效果,頻率也是隨著時間的增加而上升。為了效果,我們在Interpolator中換算時間,在Evaluator中計算位移的值。
代碼如下,只是模擬了一下,并沒有真正的按照公式來實現(xiàn)。。
public class ValueAnimatorActivity extends AppCompatActivity {
private static final String TAG = ValueAnimatorActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_value_animator);
}
@Override
protected void onResume() {
super.onResume();
final ImageView view = (ImageView) findViewById(R.id.iv_animation);
if (view != null) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
performAnimator(v);
}
});
}
}
ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
private void performAnimator(final View view) {
animator.setDuration(1000);
animator.setInterpolator(new MyInterpolator());
animator.setEvaluator(new MyEvaluator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
Log.e(TAG, "value:" + value);
if (view != null) {
view.setTranslationY(value);
}
}
});
animator.start();
}
private class MyInterpolator implements Interpolator {
// 我們需要先慢后快
@Override
public float getInterpolation(float input) {
Log.e(TAG, "input:" + input);
return (float) Math.pow(input, 3);
}
}
private class MyEvaluator implements TypeEvaluator<Float> {
private final float tau = 5.0f;
private final float omiga = 100.0f;
private final float A = 200.0f;
@Override
public Float evaluate(float fraction, Float startValue, Float endValue) {
double t = Math.exp(-fraction * tau);
double sin = Math.sin(fraction * omiga);
return (float) (A * t * sin);
}
}
}
效果如下,錄制時達不到很高的幀率,所以有點失真。

也可以直接用ObjectAnimator來實現(xiàn),這樣就不用自己設(shè)置位移了
ObjectAnimator animator = ObjectAnimator.ofFloat(view,"TranslationX",0,100);
animator.setDuration(1000);
animator.setInterpolator(new MyInterpolator());
animator.setEvaluator(new MyEvaluator());
animator.start();
總結(jié)
這樣我們的屬性動畫算是介紹完了,屬性動畫可以做出一些復(fù)雜的動畫效果,可以實現(xiàn)些有意思的效果。接下去可能會介紹自定義Drawable的實現(xiàn)。