动画中除了一些简单和组合的动画效果,还有很多其它的小功能,比如说这里的插值器、估值器、关键帧。
- 插值器
对 Android 动画来说,不管是视图动画还是属性动画,都是有插值器的,那什么是插值器呢?就是控制动画随着时间轴的变化而变换的效果。
而 Android 动画也自带了一些插值器,如:加速插值器、减速插值器、循环插值器等等,接下来我们看看 Android 动画自带的插值器,我们以循环插值器为例:
代码:
// 设置自带插值器
private void setInterpolator(){
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,"translationX",0f,200f);
animator.setDuration(2000);
// 设置自带的循环差值器,循环 2 次
animator.setInterpolator(new CycleInterpolator(2));
animator.start();
}
看一下效果:
我们来看一下它的源码,先看一下 setInterpolator() 方法的源码:
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
可以看到,它是在为 mInterpolator 赋值,那么什么时候会用到 mInterpolator 呢?我们继续看看:
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);
}
}
}
我们可以看到,上面这个方法中使用了 mInterpolator 的 getInterpolation() 方法,并把获取到的值赋给了动画的属性,那么我们看看 CycleInterpolator 的 getInterpolation() 方法:
public float getInterpolation(float input) {
return (float)(Math.sin(2 * mCycles * Math.PI * input));
}
我们看到,其实就是随着传进来的值,再返回一个随着公式变化的值,而这个 input 就代表着时间,0.0f 表示动画开始时,1.0f 表示动画结束时。
既然我们知道了这个是怎么实现的,那我们就可以自己来实现一个插值器,我们新建一个 MyInterpolator 实现 Interpolator 接口,重写 getInterpolation() 方法,然后自己找一个公式:
public class MyInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
// 这里我们使用高中经常使用的正弦表达式
return (float) (2*Math.sin(2*Math.PI*input+0.5f*Math.PI));
}
}
然后我们给动画设置我们自定义的插值器,来看看效果:
可以看到,我们已经实现了我们自定义的插值器。
- 估值器
说了插值器了,那什么是估值器呢?插值器是通过时间来得到位置,并对其进行操作的一种功能,那么估值器与之对应,估值器是返回动画当前时间的属性值,让我们来进行有些操作。
Android 动画中也有自带的估值器,如 FloatEvaluator、IntEvaluator 等等,在 API 21,Android 5.0 以后也推出了 PointFEvaluator,返回一个点的属性,可以对其 X 轴、Y 轴同时进行操作,那么如果是 Android 5.0 以前呢?我们要怎么来实现它呢?所以接下来我们通过自定义一个 PointEvaluator 来了解一下估值器的使用:
首先我们定义一个 PointT.java 文件:
public class PointT {
// 横纵坐标
private float x;
private float y;
public PointT(float x,float y){
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
接下来,我们定义一个 PointTEvaluator.java 实现 TypeEvaluator<PointTEvaluator> 接口,重写 evaluate() 方法:
public class PointTEvaluator implements TypeEvaluator<PointT> {
// 传进来的 fraction 为动画已经运行的时间,0.0 为开始,1.0 为结束
// startValue 为动画起始点坐标,endValue 为动画结束点坐标
@Override
public PointT evaluate(float fraction, PointT startValue, PointT endValue) {
// X 轴已运行距离
float d = fraction*(endValue.getX()-startValue.getX());
// 设置 X 轴坐标,为起始 X 坐标加上已经运动的 X 距离
float x = startValue.getX()+d;
// Y 轴按照正弦函数图像运行,d/200 是为了防止像素过大,运动出边界
// * 200 是因为正弦函数值域为 -1 到 1,像素大小无法分辨动画
float y = (float) (startValue.getY() + (Math.sin((d/200)*Math.PI)) * 200);
return new PointT(x,y);
}
}
接下来我们来设置估值器:
// 设置估值器
private void setEvaluator(){
ValueAnimator animator = new ValueAnimator();
animator.setDuration(2000);
// 设置起止属性
animator.setObjectValues(new PointT(imageView.getX(),imageView.getY()),
new PointT(imageView.getX()+400f,imageView.getY()));
// 设置估值器
animator.setEvaluator(new PointTEvaluator());
// 设置动画更新监听器
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 拿到估值器设置的 X、Y
PointT pointT = (PointT) animation.getAnimatedValue();
// 为 imageView 设值
imageView.setX(pointT.getX());
imageView.setY(pointT.getY());
}
});
animator.start();
}
来看看效果图:
可以看到,我们平移的时候,图像按照我们想要正弦函数图像运动,那么估值器的简单使用就是这样,还有一种写法来实现这个估值器,我们看看,比较容易懂就不做注解了:
/**
* 第二种方法设置估值器
*/
animator.setEvaluator(new TypeEvaluator<PointT>() {
@Override
public PointT evaluate(float fraction, PointT startValue, PointT endValue) {
float d = fraction*(endValue.getX()-startValue.getX());
float x = startValue.getX()+d;
float y = (float) (startValue.getY() + (Math.sin((d/200)*Math.PI)) * 200);
return new PointT(x,y);
}
});
- 关键帧
说了插值器和估值器,那么什么是关键帧呢?先看一组代码:
private void keyFrame(){
Keyframe keyframe1 = Keyframe.ofFloat(0,0);
Keyframe keyframe2 = Keyframe.ofFloat(0.25f,200);
Keyframe keyframe3 = Keyframe.ofFloat(0.75f,100);
Keyframe keyframe4 = Keyframe.ofFloat(1,300);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX",keyframe1,keyframe2,keyframe3,keyframe4);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(imageView,holder);
animator.setDuration(2000);
animator.start();
}
然后放效果图:
可以看到,我们的动画先向前,再向后,又向前,所以我们的代码什么意思呢?我们来看一下 Keyframe.ofFloat() 这个方法:
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
}
这个方法有两个参数,一个 fraction,上面我们经常使用这个参数,相信大家已经知道了,这个就是动画运行的时间,0.0 表示动画开始时,1.0 表示动画结束时,value 表示属性,这个方法就是说在给动画定义一些关键的节点赋予属性来实现动画。
好了,插值器、估值器和关键帧的简单讲解就是这么多,主要是提供一个思路,大家可以按照自己的需求实现更加复杂丰富的动画效果。
项目地址:源代码