文章目录
一.前言
前面已经介绍过了逐帧动画,补间动画,和一些属性动画的简单使用,接下来介绍一下属性动画的高级使用及工作原理。
前面的知识:
Android 动画深入分析(一)——逐帧动画,补间动画
Android 动画深入分析(二)——属性动画的简单使用
属性动画可以对任意对象进行动画操作,接下来我们就使用任意对象来实现动画。
二. 对任意属性做动画
给Button加一个动画,让button的宽度从当前值增加到500px,即对button的width属性做动画。
用上篇文章属性动画来实现:
ObjectAnimator.ofInt(button,"width",500).setDuration(5000).start();
但是发现没有效果。
先说明一下属性动画的原理:属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果去多次调用set方法,每次传递给set方法的值都不一样,准切来说是随着时间的推移,所传递的值越来越接近最终值。如果动画的时候没有传递初始值,那么还要提供get方法,因为系统要去获取属性的初始值。总结一下,我们对object的属性abc做动画,如果想让动画生效,要同时满足两个条件:
- object必须要提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法。
- object的setAbc对属性abc所做出的改变必须通过某种方法反应出来,比如会带来UI的改变之类的。
在button的内部虽然提供了getWidth和setWidth方法,但是这个setWidth方法并不改变视图的大小,即不满足条件2
解决方法:
- 给你的对象加上get和set方法,如果有条件的话
- 用一个类来包装原始对象,间接提供get和set方法
- 采用ValueAnimator,监听动画过程,自己实现属性的改变
使用上面的方法解决这个问题,第一种没有权限就不做解释
2.用一个类来包装原始对象,间接提供get和set方法
public class ViewWrapper {
private View mTarget;
public ViewWrapper(View mTarget) {
this.mTarget = mTarget;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
属性动画调用:
ObjectAnimator.ofInt(new ViewWrapper(button),"width",500).setDuration(5000).start();
解释:这里的操作对象时new ViewWrapper(button),在使用ObjectAnimator处理width属性时,会去多次调用ViewWrapper类中的set方法,而在set方法中我们对width做了处理,使其宽改变,最终达到效果。
3.采用ValueAnimator,监听动画过程,自己实现属性的改变
performAnimate(button,button.getLayoutParams().width,500);
private void performAnimate(View view, int start, int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private IntEvaluator intEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取当前动画的进度值
int currentValue = (Integer) animation.getAnimatedValue();
//获取当前动画占整个动画的比例
float fraction = animation.getAnimatedFraction();
//调用整型估值器,通过比例算出宽度,然后设给View,当然这里也可直接自己算
view.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
view.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
三.TypeEvaluator(估值器)
TypeEvaluator的作用是根据当前属性改变的百分比来计算改变后的属性值。
系统预设的有IntEvaluator(针对整型属性),FloatEvaluator(针对浮点型属性),和ArgbEvaluator(针对Color属性)。上面采用ValueAnimator,监听动画过程,自己实现属性的改变就采用了估值器
FloatEvaluator的内部实现机制
我们来看一下FloatEvaluator的代码实现:
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
可以看到,FloatEvaluator实现了TypeEvaluator接口,然后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,我们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。
自定义TypeEvaluator
ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。
1.首先自定义一个类:
public class Point {
private float x;
private float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
这个类有两个成员变量,x,y,我们的目标是让将Point1(实例对象)通过动画平滑过度到Point2(实例对象)
2.定义PointEvaluator,如下所示:
public class PointEvaluator implements TypeEvaluator{
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
float x = startPoint.getX() + fraction