Property Animation
简单应用、ofInt、ofFloat、ofObject
在Property Animation中最常见的应用就是ValueAnimator和ObjectAnimator这两个类,�它们各自私有化了�它们的构造方法,所以在实际应用中我们是通过他们的ofInt、ofFloat、ofObject方法来获取它们的实例。
我们以ObjectAnimator为例:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
public static ObjectAnimator ofInt(Object target, String propertyName, int... values)
public static ObjectAnimator ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object... values)
ofFloat与ofInt方法表示我们将要改变的属性的值是float、int类型,而当属性的值既不是float类型也不是int类型,这就需要使用ofObject
属性动画是在android3.0以后才有的,是对视图动画的一种扩展,我们先看它的几种最基本实现,然后再总结出属性动画到底在哪些方面做了改进。
switch (v.getId()) {
case R.id.translate:
ValueAnimator animator = ValueAnimator.ofInt(1);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float fraction = animation.getAnimatedFraction();
int move = new IntEvaluator().evaluate(fraction,0,500);
sample.setTranslationY(move);
}
});
animator.setDuration(1000);
animator.start();
break;
case R.id.alpha:
ObjectAnimator alpha = ObjectAnimator.ofFloat(sample,"alpha",0,1);
alpha.setDuration(1000);
alpha.start();
break;
case R.id.rotate:
ObjectAnimator rotate = ObjectAnimator.ofFloat(sample,"Rotation",0,180);
rotate.setDuration(1000);
rotate.start();
break;
case R.id.scale:
AnimatorSet set = new AnimatorSet();
ObjectAnimator scaleX = ObjectAnimator.ofFloat(sample,"scaleX",0.5f,1.5f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(sample,"scaleY",0.5f,1.5f);
set.playTogether(scaleX,scaleY);
set.setDuration(1000);
set.start();
break;
case R.id.sample:
Toast.makeText(this,"我被点击 拉",Toast.LENGTH_SHORT).show();
break;
}
动画效果如下:
通过上面的动画演示,我们能总结出以下几点:
1.通过View动画能够实现的四种效果,通过Property�动画也能实现
2.通过执行完动画后点击sample按钮可知,Property�动画是真正的改变了按钮的位置和属性
3.ObjectAnimator是ValueAnimator的子类,从实现看出,它比ValueAnimator要简单,所以在实际使用时,我们大多使用的是ObkectAnimator
4.在ofFloat方法中要改变的属性并不是随意输入的,属性动画要求对象的该属性有set方法和get方法(可选)。比如setAlpha()。
5.由于结论4可知,我们要想实现Scale效果,必须同时使用"scaleX"和"scaleY",这是因为在Button中并没有setScale()这个方法,需通过AnimatorSet来同时执行两个ObjectAnimator(还有另外一种更常用的方式——PropertyValuesHolder来实现多个动画同时执行的效果,后文中将会详细来说明)
PropertyValuesHolder
在ValueAnimator和ObjectAnimator这两个类中我们发现还有一个ofPropertyValuesHolder方法,下面将详细介绍PropertyValuesHolder这个类。
PropertyValuesHolder 这个类的意义就是,它其中保存了动画程中所需要操作的属性和对应的值。我们通ofFloat(Object target, String propertyName, float… values)构造的动画,ofFloat()的内部实现其实就是将传进来的参数封装成 PropertyValuesHolder 实例来保存动画状态。在封装成 PropertyValuesHolder 实例以后,后期的各种操作也是以 PropertyValuesHolder 为主的。 说到这里,大家就知道这个 PropertyValuesHolder 是有多有用了吧,上面我们也说了,ObjectAnimator 给我们提供了一个口子,让我们自己构造 PropertyValuesHolder 来构造动画。
下面这个应用案例将使我们更深刻的理解它
private void propertyValuesHolderAlarm(ImageView imageView) {
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("translationX",0,200);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("alpha",0,1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("rotation",0,180);
PropertyValuesHolder holder4 = PropertyValuesHolder.ofFloat("scaleX",0.5f,1.5f,1.0f);
PropertyValuesHolder holder5 = PropertyValuesHolder.ofFloat("scaleY",0.5f,1.5f);
ObjectAnimator.ofPropertyValuesHolder(imageView,holder1,holder2,holder3,holder4,holder5).
setDuration(1000).start();
}
执行的效果如下图:
插值器、估值器
要想更深入的理解android的动画机制,插值器与估值器是我们必须要深入理解和掌握的知识。
插值器
ofInt(0,400)表示在一定的时间内从0移动到400这个范围,但是我们有没有想过在每一个时间节点中执行动画的对象所在的位置是如何确定的?这个时候就需要用到我们的插值器了。首先看下他们的结构图
作用:根据时间流逝的百分比来计算出属性变化的百分比
然后看一下LinearInterpolator的实现
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();
}
}
这里最核心的一个方法就是getInterpolation(float input),他根据传入过来的一个input通过一系列的计算后返回一个值,这个值就是计算出来的依据当前的动画执行进度所确定的位置的一个计算值。如果用一句话来总结插值器的作用,那应该就是插值器就是用来控制动画区间的值被如何计算出来的。比如 LinearInterpolator 插值器就是匀速返回区间点的值;而 DecelerateInterpolator 则表示开始变化快,后期变化慢;
下面这个图片就是运用了插值器之后的效果:
估值器
在学习估值器之前我们先看一张图片
这幅图讲述了从定义动画的数字区间到通过 AnimatorUpdateListener 中得到当前动画所对应数值的整个过程。下面我们对这四个步骤具体讲解一下: (1)、ofInt(0,400)表示指定动画的数字区间,是从 0 运动到 400; (2)、加速器:上面我们讲了,在动画开始后,通过加速器会返回当前动画进度所对应的数字进度,但这个数字进度是百分制的,以小数表示,如 0.2 (3)、Evaluator:我们知道我们通过监听器拿到的是当前动画所对应的具体数值,而不是百分制的进度。那么就必须有一个地方会根据当前的数字进度,将其转化为对应的数值,这个地方就是 Evaluator;Evaluator 就是将从加速器返回的数字进度转成对应的数字值。所以上部分中,我们讲到的公式:
当前的值 = 100 + (400 - 100)* 显示进度
这个公式就是在 Evaluator 计算的;在拿到当前数字进度所对应的值以后,将其返回 (4)、监听器:我们通过在 AnimatorUpdateListener 监听器使用 animation.getAnimatedValue()函数拿到 Evaluator 中返回的数字值。 讲了这么多,Evaluator 其实就是一个转换器,他能把小数进度转换成对应的数值位置
依据属性的变化的百分比计算出具体的数值
在一般的请款下插值器使用系统默认的插值器已足够我们日常使用,但只要属性类型不是int、float、argb(颜色)我们常常需要自定义估值器。
应用案例
我们知道,在属性动画里能够改变的属性必须具备setXXX()和getXXX()方法(在某些情况下也可以没有),可是在有些情况下我们就需要改变的属性偏偏就没有这些方法,这时我们该怎么办?比如我们需要执行一个动画效果:改变TextView的字符类型,这个方法在TextView中并未提供相应set和get方法,在这种情况下,我们一般有如下三种解决方式:
- �给你的对象加上set和get方法,如果你有权限的话
- 用一个类包装原始对象,间接为其提供set和get方法
- 采用ValueAnimator,监听动画过程,自己实现属性的变化
上面三种解决方案,以后两种在日常使用中使用的频率较高。下面就是上面案例解决的代码
final ObjectAnimator objectAnimator = ObjectAnimator.ofObject(new TextViewWapper(tv)
, "char", new CharEvaluator(), new Character('A'), new Character('Z'));
objectAnimator.setDuration(2000);
objectAnimator.setInterpolator(new AccelerateInterpolator());
objectAnimator.start();
--------------------
public class CharEvaluator implements TypeEvaluator<Character> {
public Character evaluate(float fraction, Character startValue, Character endValue) {
int startInt = (int)startValue;
int endInt = (int)endValue;
final int i = (int) (startInt + fraction * (endInt - startInt));
char result = (char) i;
return result;
}
}
-------------------
class TextViewWapper{
private TextView mTarget;
public TextViewWapper(TextView mTarget) {
this.mTarget = mTarget;
}
public void setChar(Character character) {
mTarget.setText(String.valueOf(character));
}
}
动画执行效果如下:
KeyFrame
我们知道如果要控制动画速率的变化,我们可以通过自定义插值器,也可以通过自定义 Evaluator 来实现。但如果真的让我们为了速率变化效果而自定义插值器或者 Evaluator 的话,恐怕大部分同学会有一万头草泥马在眼前奔过,因为大部分的同学的数学知识已经还给老师了。 为了解决方便的控制动画速率的问题,谷歌定义了一个 KeyFrame 的类,KeyFrame 直译过来就是关键帧。 关键帧这个概念是从动画里学来的,我们知道视频里,一秒要播放 24 帧图片,对于制作 flash 动画的同学来讲,是不是每一帧都要画出来呢?当然不是了;比如我们要让一个球在 30 秒时间内,从(0,0)点运动到(300,200)点,那 flash 是怎么来做的呢,在 flash 中,我们只需要定义两个关键帧,在动画开始时定义一个,把球的位置放在(0,0)点;在 30 秒后,再定义一个关键帧,把球的位置放在(300,200)点。在动画 开始时,球初始在是(0,0)点,30 秒时间内就 adobe flash 就会自动填充,把球平滑移动到第二个关键帧的位置(300,200)点; 通过上面分析 flash 动画的制作原理,我们知道,一个关键帧必须包含两个原素,第一时间点,第二位置。即这个关键帧是表示的是某个物体在哪个时间点应该在哪个位置上。 所以谷歌的 KeyFrame 也不例外,KeyFrame 的生成方式为:
Keyframe kf0 = Keyframe.ofFloat(0, 0);
Keyframe kf1 = Keyframe.ofFloat(0.1f, -20f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0);
public static Keyframe ofFloat(float fraction, float value)
fraction:表示当前的显示进度,即从加速器中 getInterpolation()函数的返回值;
value:表示当前应该在的位置 比如 Keyframe.ofFloat(0, 0)表示动画进度为 0 时,动画所在的数值位置为 0;Keyframe.ofFloat(0.25f, -20f)表示动画进度为 25%时,动画所在的数值位置为-20;Keyframe.ofFloat(1f,0)表示动画结束时,动画所在的数值位置为 0; 在理解了 KeyFrame.ofFloat()的参数以后,我们来看看 PropertyValuesHolder 是如何使用 KeyFrame 对象的:
Keyframe frame0 = Keyframe.ofFloat(0f, 0);
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);
Keyframe frame2 = Keyframe.ofFloat(1, 0);
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2);
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder);
animator.setDuration(1000);
animator.start();
动画的执行效果如下:
总结
1.Property Animator 是通过改变控件某一属性值来做动画的
2.Property Animator 能实现补间动画无法实现的功能
3.补间动画虽能对控件做动画,但并没有改变控件内部的属性值。而 Property Animator 则是恰恰相反,Property Animator 是通过改变控件内部的属性值来达到动画效果的,视图动画只是改变了View得绘制位置,并没有改变View的位置本身。比如一个Button通过位移动画移动到一个位置,但是你在原位置能执行点击动画,因为button本身并没有发生变化
With the property animation system, these constraints are completely removed, and you can animate any property of any object (Views and non-Views) and the object itself is actually modified.
The view animation system, however, takes less time to setup and requires less code to write.
所以如果视图动画能够满足你的要求,用视图动画
4.属性动画最大的问题也许就是兼容性了,因为它只适用于android3.0以上的系统,使用nineoldandroids可以兼容android3.0一下系统,可是其本质仍然是视图动画。