概述
在认识动画第一节我们就提到了这个属性动画(Property Animator),也了解了它与视图动画的差异,但是大家有没有想过为已经有了View Aniamtion还要再引入一个Property呢?接下来我们就先讨论下,它的存在必要性
1.为什么引入Property Animator(属性动画)
既然引入了Property Animator就说明View Animation(这里实际指得是补间动画)有不能达到的效果,例如我们想将一个控件的背景颜色1秒之内从红色渐变为绿色,这个效果很明显利用补间动画的透明化,移动,旋转,放大缩小功能所不能完成的,而这个效果可以通过Property Animator完美实现.
- 这就是第一个原因,属性动画能做到补间动画做不到的事.
- 至于第二点原因,补件动画是对指定的控件做动画,而Property则可以改变某一控件的属性值.
- 而第三点原因与第二条有很大的关系,上面我们也说了,补间动画只能对控件进行动画,而属性动画是针对属性的,所以补间动画并没有改变控件的任何属性,而属性动画则是直接改变了动画的属性来进行动画.
举个栗子:
假如我们使用TrabslateAnimation使一个已经设置了事件监听的按钮从(0,0)移动到(50,50)此时,点击在(50,50)的按钮并不能激活点击事件,然而当我们点击(0,0)原来按钮的位置时仍会激活点击事件.这就说明了对于Button来讲,虽然经历了动画,但实际上它的属性没有任何改变.
ValueAnimator
使用方式
ValueAnimator的使用方式很简单,一共只需要两步:
- 创建ValueAnimator实例
ValueAnimator animator = ValueAnimator.ofInt(0,400);
animator.setDuration(1000);
animator.start();
在这里我们可以看到我们定义了一个0-400的动画.动画时长是1s,然后动画开始.从代码中可以看出整个过程和控件没有任何的关联,那也正好说明了ValueAnimator只是对值做动画运算,而不是针对控件的,我们需要监听ValueAnimator的动画过程自己对控件做操作.
- 添加监听
上面的3行代码,我们已经实现了动画,下面我们就对动画添加监听:
ValueAnimator animator = ValueAnimator.ofInt(0,400);
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (int)animation.getAnimatedValue();
// 利用得到的curValue值就是当前View要操作的属性的当前值
}
});
animator.start();
上面代码中我们对动画添加了一个addUpdateListener方法监听,在监听回传的结果中,是表示当前的ValueAnimator实例,我们通过animation.getAnimatedValue()获得当前值来设置动画属性.
这就是ValueAnimator的功能,他只负责数值从起点到终点的变化,而我们需要通过对数值变化的监听改变控件的属性值,完成动画.总结起来就两点:
- ValueAniamtor只负责对指定的数字区间进行动画运算
- 我们需要对运算过程进行监听,然后自己对控件做动画操作
常用方法
ValueAniamtor常用的方法如下:
/**
* 设置动画时长,单位是毫秒
*/
ValueAnimator setDuration(long duration)
/**
* 获取ValueAnimator在运动时,当前运动点的值
*/
Object getAnimatedValue();
/**
* 开始动画
*/
void start()
/**
* 设置循环次数,设置为INFINITE表示无限循环
*/
void setRepeatCount(int value)
/**
* 设置循环模式
* value取值有RESTART,REVERSE,
*/
void setRepeatMode(int value)
/**
* 取消动画
*/
void cancel()
两个监听器
- 前面我们讲过一个添加监听器animator.addUpdateListener,以监听动画过程中值的实时变化,其实在ValueAnimator中共有两个监听器:
/**
* 监听器一:监听动画变化时的实时值
*/
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
//添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
/**
* 监听器二:监听动画变化时四个状态
*/
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//添加方法为:public void addListener(AnimatorListener listener)
关于监听器一:AnimatorUpdateListener就是为监听动画的实时变化状态,在onAnimationUpdate中得到了代表当前动画状态的实例.我们就不详讲了,主要讲一下下面这个AnimatorListener:
相信大家也能看出来,AnimatorListener主要监听动画播放的四个状态,start,end, cancel,repeat;当动画开始,结束,取消,重复时分别会调用上述方法.
- 取消监听
上面我们讲了如何添加监听,接下来我们来看如何移除监听器:
void removeUpdateListener(AnimatorUpdateListener listener);
void removeAllUpdateListeners();
/**
* 移除AnimatorListener
*/
void removeListener(AnimatorListener listener);
void removeAllListeners();
针对AnimatorUpdateListener和AnimatorListener,每个监听都有两个方法来移除;我们就移除AnimatorListener,removeListener(AnimatorListener listener)用于在animator中移除指定的监听器,而removeAllListeners()用于移除animator中所有的AnimatorListener监听器;
- 其他函数
上面我们讲了ValueAnimator中常用的一些函数,但是还有一些函数虽然不常用,但我们还是简单讲一下,他们分别是:
/**
* 延时多久时间开始,单位是毫秒
*/
public void setStartDelay(long startDelay)
/**
* 完全克隆一个ValueAnimator实例,包括它所有的设置以及所有对监听器代码的处理
*/
public ValueAnimator clone()
setStartDelay方法非常容易理解,让我们的动画延迟几秒再开始播放,我们重点看一下clone()方法.首先什么叫克隆,克隆就是完全一样,什么叫完全一样,就是本体有的克隆出来的实例也有,包括事件监听.不过克隆完毕后,对本体操作,却不会影响克隆体,虽然一样,但是相互独立.
ofObject
上面我们讲了ofInt()和ofFloat来定义动画,但ofInt()只能传入Integer类型的值,而ofFloat()则只能传入Float类型的值.那如果我们需要操作其他类型的变量要怎么办?其实ValueAnimator还有一个函数ofObject(),可以传进去任何类型的变量,定义如下:
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);
它有两个参数,第一个是自定义的Evaluator,第二个可变长参数,Object类型的;
大家可能会疑问,为什么要强制传进去自定义的Evaluator?首先,大家知道Evaluator的作用是根据进度到值的转换过程也必须由我们来做,不然系统哪知道你要干嘛.下来我们看看这个ofObject怎么用?
ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
char text = (char)animation.getAnimatedValue();
tv.setText(String.valueOf(text));
}
});
animator.setDuration(10000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
这里注意三点:
- 构造时:传入的第一个参数是我们自定义的一个CharEvaluator,关于这个类的实现后面会详细说明,这里只需要知道他是一个自定义Evaluator就行,后面两个参数是Character对象,表示动画的起始点.
这样这个动画要做的事情就一目了然了,他无非就是要使这个动画参数从a->z.具体怎么实现,那就是我们自定义的Evaluator的事了. - 看监听:通过animation.getAnimatedValue()得到的是char类型的值,然后吧字符显示到TextView,又上一条我们看到构造时传入的是Character对象,所以在动画过程通过Evaluator返回的值必然跟构造时类型一致.
- 插值器:我们使用的是加速插值器,随着动画的进行,速度会越来越快,这点跟我们上面的效果图是一致的,最关键的就要看我们自定义的CharEvaluator.
我们知道在ASCII码中,每个字符都有数字跟他一一对应,字母A到字母Z之间的所有字母对应的数字区间为65到90:
而且在程序中,我们能通过数字强转成对应的字符.
数字转字符
char temp = (char)65;
字符转数字
char temp = 'A';
int num = (int)temp;
在这里就用到了数字与字符转换,主要代码如下:
public class CharEvaluator implements TypeEvaluator<Character> {
@Override
public Character evaluate(float fraction, Character startValue, Character endValue) {
int startInt = (int)startValue;
int endInt = (int)endValue;
int curInt = (int)(startInt + fraction *(endInt - startInt));
char result = (char)curInt;
return result;
}
}
将字符转换成数字,然后对渐变的字母进行输出.
除了可以使用字符作为参数,同样也可以使用自定义类型.
ObjectAnimator
基本使用
上面给大家介绍了ValueAnimator,但ValueAnimator有个缺点,就是只能对数值对动画计算.我们想要哪个控件操作,需要监听动画过程,在监听中对控件操作.这样使用对补间动画而言就相对比较麻烦.
为了能让动画直接与对应的控件相关联,以是我们监听动画过程中解放出来,我们的ObjectAniamtor中所能使用的方法,在ObjectAnimator中都可以正常使用.但ObjectAnimator也重写了几个方法,比如ofInt(),ofFloat()等.我们先看看利用ObjectAnimator重写的ofFloat()等,我们先看看使用ObjectAnimator重写的ofFloat方法如何实现一个动画:(改变透明度)
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);
animator.setDuration(2000);
animator.start();
这里我还是先了解构造动画方法参数
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
- 第一个参数用于指定这个动画操作的是哪个控件
- 第二个参数用于指定这个动画要操作这个控件的哪个属性
- 第三个参数是可变长参数,这个就是跟ValueAniamtor中可变长参数的意义一样,就是指这个属性从哪变到哪.
setter函数
我们再回过头来看看改变alpha值的ObjectAnimator方法
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);
TextView控件有alpha这个属性吗?没有,不光TextView没有,就连他的父类View也没有.那它怎么来改变这个值的呢?其实,ObjectAniamtor做动画,并不是根据控件中xml的属性来改变的,而是通过指定属性所对应的set方法来改变的.比如上面指定的改变alpha的属性值,ObjectAnimator在做动画时就会到指定控件(TextView)中去找对应的setAlpha()方法来改变控件中对应的值.同样的道理,在我们
指定改变"xxxx"值的时候,ObjectAnimator也会到TextView中找对应的setXxxx()方法.而这些方法都是从View哪里继承过来的.在View中有关动画,总共有下面几组set方法.
//1、透明度:alpha
public void setAlpha(float alpha)
//2、旋转度数:rotation、rotationX、rotationY
public void setRotation(float rotation) 表示围绕Z旋转,rotation表示旋转度数
public void setRotationX(float rotationX) 表示围绕X轴旋转,rotationX表示旋转度数
public void setRotationY(float rotationY) 表示围绕Y轴旋转,rotationY表示旋转度数
//3、平移:translationX、translationY
public void setTranslationX(float translationX) 表示在X轴上的平移距离,以当前控件为原点,向右为正方向,参数translationX表示移动的距离。
public void setTranslationY(float translationY) 表示在Y轴上的平移距离,以当前控件为原点,向下为正方向,参数translationY表示移动的距离。
//缩放:scaleX、scaleY
public void setScaleX(float scaleX) 在X轴上缩放,scaleX表示缩放倍数
public void setScaleY(float scaleY) 在Y轴上缩放,scaleY表示缩放倍数
现在我们可以通过使用ObjectAniamtor方法直接对View属性做动画设置.在开始使用这些函数之前,我们先来做一个总结:
- 要使用ObjectAnimator来构造动画,要操作的控件中,必须存在对应的属性的set方法
- setter方法的命名必须以骆驼拼写法命名,即set后每个首字母大写,其余字母小写,即类似于setPropertyName所对应的属性为propertyName
ObjectAnimator动画原理
我们先来看一张图:
在这张图中我们将valueAnimator与ObjectAnimator的流程相对比,我们可以看出前三步两者的步骤一致,区别就在于最后一步.那么我们在对比一下两者的使用过程即可知道在ObjectAnimator的最后一步,为我们做了什么.好了不兜圈子了,接下来让我们一起看看他究竟是怎样替我们完成View属性实时操控.首先我们在构造Animator时获得了一个String的属性名,他可以利用这个属性名假设是
xxxx
,通过反射机制在控件中找到对应的setXxxx
方法然后将Evaluator获得的数值传入该方法以改变控件的属性,达到动画的效果.根据上面的叙述,这里有几个注意事项:
- 拼接set函数的方法:上面我们也说了是首先是强制将属性的第一个字母大写,然后与set拼写,就是对应的set函数的名字,注意,只是强制将属性的第一个字母大写,后面的部分时保持不变的.反过来,如果我们函数命名为setScalePointX(float)那我们在写属性时可以写成'scalePoint'或者
ScalePoint
都可以,即第一个字母大小写可以随意,但是后面的必须与原方法名保持一致 - 如何确定函数的参数类型:上面我们知道如何找到对应的函数名,那对应的参数方法应该如何确定呢?我们在讲
ValueAnimator
时候说过,在动画过程中产生的数值与构造时传入的值的类型一致,而当前动画产生的值都是在Evaluator这一步产生的,所以ObjectAnimator的动画中产生的数值类型也是与构造时的类型一样. - 调用set函数以后怎么办:从ObjectAnimator的流程可以看到,到这一步已经结束了,关于set函数内部如何操作还需要我们自己操作.我们来看看View中的setScaleY是怎么实现的:
public void setScaleY(float scaleY) {
ensureTransformationInfo();
final TransformationInfo info = mTransformationInfo;
if (info.mScaleY != scaleY) {
invalidateParentCaches();
// Double-invalidation is necessary to capture view's old and new areas
invalidate(false);
info.mScaleY = scaleY;
info.mMatrixDirty = true;
mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
invalidate(false);
}
}
上面代码就做了两件事:第一步,将新的参数赋值给控件属性,第二部调用Invalidate()强制重绘.
注意:
仅且仅当我们只给动画设置一个值时,我们需要实现属性的get方法,因为此时程序才会调用属性对应的get函数来得到动画初始值。如果动画没有初始值,那么就会使用系统默认值。
常用函数
/**
* 设置动画时长,单位是毫秒
*/
ValueAnimator setDuration(long duration)
/**
* 获取ValueAnimator在运动时,当前运动点的值
*/
Object getAnimatedValue();
/**
* 开始动画
*/
void start()
/**
* 设置循环次数,设置为INFINITE表示无限循环
*/
void setRepeatCount(int value)
/**
* 设置循环模式
* value取值有RESTART,REVERSE,
*/
void setRepeatMode(int value)
/**
* 取消动画
*/
void cancel()
监听器
/**
* 监听器一:监听动画变化时的实时值
*/
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
//添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
/**
* 监听器二:监听动画变化时四个状态
*/
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//添加方法为:public void addListener(AnimatorListener listener)
该内容与上述ValueAnimator一致,没有必要重复介绍.
属性动画的基础使用我们就认识到这里,希望能帮到大家
文章来自:
http://blog.csdn.net/harvic880925/article/details/50598322
网友评论