Interpolator 是被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等。
Interpolator中文意思是插补器(百度翻译),而用在Android动画中,可以理解为是用于动画中的时间插值,其作用就是把0到1的浮点值变化映射到另一个浮点值变化。
插补器分为:
AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后然后向前甩
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置
我们如果要实现一个动画效果,可以继承Animation,然后在applyTransformation(float interpolatedTime, Transformation t)这个方法内去实现具体效果,而这个方法的第一个参数interpolatedTime就是动画时间,Interpolator的作用就是改变这个时间。
我们可以从源码看出,当一个动画调用了setInterpolator(Interpolator i),设置了插补器后,它是在什么时候启作用的呢?查看Animation.java这个动画父类,getTransformation(long currentTime, Transformation outTransformation),
public boolean getTransformation(long currentTime, Transformation outTransformation) { if (mStartTime == -1) { mStartTime = currentTime; } final long startOffset = getStartOffset(); final long duration = mDuration; float normalizedTime; if (duration != 0) { normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) / (float) duration; } else { // time is a step-change with a zero duration normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f; } final boolean expired = normalizedTime >= 1.0f; mMore = !expired; if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) { if (!mStarted) { fireAnimationStart(); mStarted = true; if (USE_CLOSEGUARD) { guard.open("cancel or detach or getTransformation"); } } if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f); if (mCycleFlip) { normalizedTime = 1.0f - normalizedTime; } final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); applyTransformation(interpolatedTime, outTransformation); } if (expired) { if (mRepeatCount == mRepeated) { if (!mEnded) { mEnded = true; guard.close(); fireAnimationEnd(); } } else { if (mRepeatCount > 0) { mRepeated++; } if (mRepeatMode == REVERSE) { mCycleFlip = !mCycleFlip; } mStartTime = -1; mMore = true; fireAnimationRepeat(); } } if (!mMore && mOneMoreTime) { mOneMoreTime = false; return true; } return mMore; }看37、38行那两句,mInterpolator变量就是我们前面调用 setInterpolator设置的插补器对象,然后将mInterpolator.getInterpolation()的返回值当作第一个参数去执行applyTransformation,所以我们可以看出Interpolator对动画变化效果起到辅助作用的。我们就用平移动画效果来讲Interpolator是怎么起辅助作用的。
看下TranslateAnimation的applyTransformation方法
protected void applyTransformation(float interpolatedTime, Transformation t) { float dx = mFromXDelta; float dy = mFromYDelta; if (mFromXDelta != mToXDelta) { dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime); } if (mFromYDelta != mToYDelta) { dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime); } t.getMatrix().setTranslate(dx, dy); }
,这个插补器的变化是怎样的呢?
public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; } else { return (float)Math.pow(input, mDoubleFactor); } }
public AccelerateInterpolator(float factor) { mFactor = factor; mDoubleFactor = 2 * mFactor; }
接下来我们看下效果
public class InterpolatorAnimationActivity extends Activity implements AdapterView.OnItemSelectedListener { private static final String[] INTERPOLATORS = { "Accelerate", "Decelerate", "Accelerate/Decelerate", "Anticipate", "Overshoot", "Anticipate/Overshoot", "Bounce", "LinearInterpolator", "CycleInterpolator" }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_interpolator_animation); Spinner s = (Spinner) findViewById(R.id.spinner); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, INTERPOLATORS); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); s.setAdapter(adapter); s.setOnItemSelectedListener(this); } public void onItemSelected(AdapterView<?> parent, View v, int position, long id) { final View target = findViewById(R.id.target); final View targetParent = (View) target.getParent(); Animation a = new TranslateAnimation(// 平移动画 0.0f, targetParent.getWidth() - target.getWidth() - targetParent.getPaddingLeft() - targetParent.getPaddingRight(), 0.0f, 0.0f); a.setDuration(1000); a.setStartOffset(300); a.setRepeatMode(Animation.RESTART); a.setRepeatCount(Animation.INFINITE); switch (position) { case 0: // 加速插值器 速度变化公式y=x^2(默认) a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.accelerate_interpolator)); break; case 1: // 减速插值器 a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.decelerate_interpolator)); break; case 2: // 加速减速插值器 a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.accelerate_decelerate_interpolator)); break; case 3: a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.anticipate_interpolator)); break; case 4: a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.overshoot_interpolator)); break; case 5: a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.anticipate_overshoot_interpolator)); break; case 6: a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.bounce_interpolator)); break; case 7: a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.linear_interpolator)); break; case 8: a.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.cycle_interpolator)); break; } target.startAnimation(a); } public void onNothingSelected(AdapterView<?> parent) { } }activity_interpolator_animation.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="10dip" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false"> <TextView android:id="@+id/target" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="26sp" android:text="Interpolators"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dip" android:layout_marginBottom="5dip" android:text="Select an animation" /> <Spinner android:id="@+id/spinner" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>