文章概览
本系列将介绍以下内容:
1 插值器Interpolator
1.1 Interpolator概述
有关动画的变化速率的问题是由Interpolator接口决定的,它是用来指定动画如何变化的变量,即用来控制动画的区间值是如何被计算出来的。
哪些动画可以设置插值器呢?
可以:Tween Animation、ValueAnimator、ObjectAnimator、ViewPropertyAnimator
不可以:Frame Animation
即只有逐帧动画不能设置插值器Interpolator。
View Animation中,补间动画的父类Animation中有具体方法setInterpolator(Interpolator i),而逐帧动画的实现类AnimationDrawable没有相关方法,即补间动画可以设置插值器,而逐帧动画不能。
Property Animation均可设置插值器,因为ValueAnimator和ObjectAnimator的父类Animator中有抽象方法setInterpolator(TimeInterpolator value),而ViewPropertyAnimator中单独定义了非抽象方法setInterpolator(TimeInterpolator interpolator)。
Interpolator接口的父接口是TimeInterpolator,而常用的插值器实现类全部继承自抽象类BaseInterpolator。
TimeInterpolator位于包android.animation中,Interpolator、BaseInterpolator及其十个子类全部位于包android.view.animation中。
关于Interpolator接口的UML图(类图Class Diagram)如图所示:
package android.animation;
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
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);
}
package android.view.animation;
import android.animation.TimeInterpolator;
/**
* An interpolator defines the rate of change of an animation. This allows
* the basic animation effects (alpha, scale, translate, rotate) to be
* accelerated, decelerated, repeated, etc.
*/
public interface Interpolator extends TimeInterpolator {
// A new interface, TimeInterpolator, was introduced for the new android.animation
// package. This older Interpolator interface extends TimeInterpolator so that users of
// the new Animator-based animations can use either the old Interpolator implementations or
// new classes that implement TimeInterpolator directly.
}
package android.view.animation;
import android.content.pm.ActivityInfo.Config;
/**
* An abstract class which is extended by default interpolators.
*/
abstract public class BaseInterpolator implements Interpolator {
private @Config int mChangingConfiguration;
/**
* @hide
*/
public @Config int getChangingConfiguration() {
return mChangingConfiguration;
}
/**
* @hide
*/
void setChangingConfiguration(@Config int changingConfiguration) {
mChangingConfiguration = changingConfiguration;
}
}
1.2 常用插值器
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
常用的Interpolator实现类有十个,全部继承自抽象类BaseInterpolator,且均位于包android.view.animation中:
package android.view.animation;
import android.content.Context;
import android.graphics.animation.HasNativeInterpolator;
import android.graphics.animation.NativeInterpolator;
import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolator {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({
"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactory.createAccelerateDecelerateInterpolator();
}
}
package android.view.animation;
@HasNativeInterpolator
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
...
}
package android.view.animation;
@HasNativeInterpolator
public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolator {
...
}
package android.view.animation;
@HasNativeInterpolator
public class AnticipateOvershootInterpolator extends BaseInterpolator
implements NativeInterpolator {
...
}
package android.view.animation;
@HasNativeInterpolator
public class BounceInterpolator extends BaseInterpolator implements NativeInterpolator {
...
}
package android.view.animation;
@HasNativeInterpolator
public class CycleInterpolator extends BaseInterpolator implements NativeInterpolator {
...
}
package android.view.animation;
@HasNativeInterpolator
public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
...
}
package android.view.animation;
import android.content.Context;
import android.graphics.animation.HasNativeInterpolator;
import android.graphics.animation.NativeInterpolator;
import android.graphics.animation.NativeInterpolatorFactory;
import android.util.AttributeSet;
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactory.createLinearInterpolator();
}
}
package android.view.animation;
@HasNativeInterpolator
public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolator {
...
}
package android.view.animation;
@HasNativeInterpolator
public class PathInterpolator extends BaseInterpolator implements NativeInterpolator{
...
}
1.2.1 在XML中引用插值器
插值器的使用方法有两种,一种是在XML中直接引用,一种是用代码设置。
用法一,在XML文件中直接引用,如
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillBefore="true"
android:fromAlpha="1.0"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="0.1">
</alpha>
1.2.2 通过setInterpolator(xxx)设置插值器
用法二,通过Animation.java中的setInterpolator(xxx)设置,如
alphaAnim.setInterpolator(new LinearInterpolator());
1.3 自定义插值器
从Interpolator的类图结构可以看出,想要自定义插值器只需实现TimeInterpolator接口即可,其用法与普通插值器类似,请浏览以下Demo:
创建一个自定义的Interpolator类
package com.example.InterploatorEvaluator;
import android.animation.TimeInterpolator;
public class MyInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
return 1 - input;
}
}
使用时直接调用相关的setInterpolator()即可
xxxAnimation.setInterpolator(new MyInterpolator());
xxxAnimator.setInterpolator(new MyInterpolator());
自定义插值器就这么简单。
2 转换器Evaluator
2.1 Evaluator简介
Evaluator可译为求值程序,在动画中相当于一个转换器,用于将插值器返回的数值(小数)进度转换成对应的数值(位置)。
类似于Interpolator,哪些动画能设置Evaluator呢?
可以:ValueAnimator、ObjectAnimator
不可以:Tween Animation、Frame Animation、ViewPropertyAnimator
即只有常用属性动画ValueAnimator和ObjectAnimator能设置转换器Evaluator。
从源码分析,ValueAnimator中有setEvaluator(TypeEvaluator value)方法,而ObjectAnimator继承自ValueAnimator。其余动画类中均没有设置evaluator的方法。
鉴于此,分析Evaluator仍需借助于ValueAniamtor的类图:
package android.animation;
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
package java.lang;
public interface Cloneable {
}
package android.animation;
import java.util.List;
public interface Keyframes extends Cloneable {
void setEvaluator(TypeEvaluator evaluator);
Class getType();
Object getValue(float fraction);
List<Keyframe> getKeyframes();
Keyframes clone();
public interface IntKeyframes extends Keyframes {
int getIntValue(float fraction);
}
public interface FloatKeyframes extends Keyframes {
float getFloatValue(float fraction);
}
}
package android.animation;
public class PropertyValuesHolder implements Cloneable {
Keyframes mKeyframes = null;
private TypeEvaluator mEvaluator;
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
mKeyframes.setEvaluator(evaluator);
}
}
package android.animation;
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
/**
* The property/value sets being animated.
*/
PropertyValuesHolder[] mValues;
public void setEvaluator(TypeEvaluator value) {
if (value != null && mValues != null && mValues.length > 0) {
mValues[0].setEvaluator(value);
}
}
}
经源码分析,Evaluator最终由ValueAnimator中的关联关系类PropertyValuesHolder设置。
2.2 Evaluator用法
Evaluator的使用比较简单,直接调用ValueAnimator中的setEvaluator(TypeEvaluator value)方法即可,详见Demo。
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始动画" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_toRightOf="@id/start_anim"
android:background="#ffff00"
android:text="看我怎么动"
android:textSize="18sp" />
</RelativeLayout>
代码示例:
package com.example.InterploatorEvaluator;
import android.animation.IntEvaluator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
public class InterploatorActivity extends Activity {
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.interpolater_activity);
tv = (TextView) findViewById(R.id.tv);
findViewById(R.id.start_anim).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 初始化时传入的是ofInt类型的数值
ValueAnimator animator = ValueAnimator.ofInt(0, 300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (Integer) animation.getAnimatedValue();
tv.layout(tv.getLeft(), curValue, tv.getRight(), curValue + tv.getHeight());
}
});
animator.setDuration(1000);
// 线性变化的插值器
animator.setInterpolator(new LinearInterpolator());
// 设置对应的Int类型的Evaluator
animator.setEvaluator(new IntEvaluator());
animator.start();
}
});
}
}
效果图:
由于Evaluator是把插值器Interpolator返回的小数进度转换成当前数值进度所对应的值。因此如果使用ofInt()方法来定义动画,如ValueAnimator.ofInt(0, 300),那么所对应的Evaluator在返回值时必然返回Integer类型的值,即每种定义方式所对应的Evaluator必然是它专用的(只有定义动画时的数值类型与Evaluator的返回值类型一样,才能使用这个Evaluator)。因为动画数值类型不一样,在通过Evaluator返回时会报强转错误,只有在动画类型一样时,所对应的Evaluator才能通用。
系统有几个默认的Evaluator,如ofInt(xxx)对应IntEvaluator,ofFloat(xxx)对应FloatEvaluator等等,上述Demo中使用的是ValueAnimator.ofInt(0, 300),因此设置Evaluator时是animator.setEvaluator(new IntEvaluator())。
2.3 自定义Evaluator
想要自定义Evaluator只需要分析一下系统内置的IntEvaluator源码即可找到思路:
package android.animation;
/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* 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; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
源码很少,可以看到要自定义Evaluator只需要实现泛型接口TypeEvaluator< T >即可,而重要的是evaluate(xxx)中的代码怎么写?
参数分析:
1、fraction参数是插值器中的返回值,表示当前动画的数值进度,以百分制的小数表示。
2、startValue和endValue分别对应ofInt(int start, int end)中的start和end的值。例如定义的动画ofInt(100, 300)进行到数值进度20%的时候,则evaluate(xxx)中的fraction = 0.2,startValue = 100,endValue = 300。
3、evaluate(xxx)的函数返回值就是当前数值进度所对应的具体数值(自定义Evaluator时可自定义计算方法),该值就是在AnimatorUpdateListener监听器中通过animation.getAnimatedValue()得到的数值。
自定义Evaluator的用法参考上文Demo,直接调用setEvaluator(new MyEvaluator())即可,并无区别。
3 Interpolator与Evaluator的区别
分析一下属性动画中ValueAnimator的执行流程:
上图所示流程共四步:
1、ValueAnimator.ofInt(0, 400),指定动画的数值区间,从0到400
2、插值器Interpolator,在动画开始后,通过插值器返回当前动画进度所对应的数值进度。但该数值进度是以小数表示的,如0.2。
3、转换器Evaluator,通过监听器拿到的是当前动画所对应的具体数值,而不是用小数表示的数值。那么必须有一个地方会根据当前的数值进度将其转换成对应的数值,这个地方就是Evaluator。因此Evaluator用于将插值器返回的数值进度转换成对应的数值。
4、监听器返回,通过在ValueAnimator.AnimatorUpdateListener监听器中使用animation.getAnimatedValue()拿到Evaluator中返回的数值。
即,Evaluator就是一个转换器,它能把小数进度转换成对应的数值位置。
解读上述执行流程发现,在插值器Interpolator中,可以通过自定义插值器返回的数值进度来改变返回数值的位置;在转换器Evaluator中,又可以通过改变数值进度所对应的具体数值来改变数值的位置,因此结论就是:
既可以通过重写Interpolator改变数值进度来改变数值位置,也可以通过改变Evaluator中数值进度所对应的具体数值来改变数值位置(插值器只能改变动画进展的快慢,转换器可以改变返回的值)。
参考文献:
[1] UML中的类图及类图之间的关系
[2] 启舰.Android自定义控件开发入门与实战[M].北京:电子工业出版社,2018
微信公众号:TechU