上一篇【Android 动画】View Animation详解(一)我们介绍了Android View Animation动画,包括Tween动画和帧动画,今天我们来介绍一下另一种非常 好用的动画——-Property Animation(属性动画)。
一.属性动画概念
所谓属性动画,就是通过不断修改组件的私有属性来调整组件的大小,位置,缩放,清晰度等等效果,从而达到一个动画的效果,属性动画可以轻而易举的办到许多View动画做不到的事,今天我们就来学习一下属性动画。
首先我们要先了解关于View在3.0之后引入的几个新的属性,并设置了其getter和setter方法:
translationX 和 translationY:这两个属性控制了View所处的位置,它们的值是由layout容器设置的,是相对于坐标原点(0,0左上角)的一个偏移量。
rotation, rotationX 和 rotationY:控制View绕着轴点(pivotX和pivotY)旋转.
scaleX 和 scaleY:控制View基于pivotX和pivotY的缩放。
pivotX 和 pivotY:旋转的轴点和缩放的基准点,默认是View的中心点。
x 和 y:描述了view在其父容器中的最终位置,是左上角左标和偏移量(translationX,translationY)的和。
aplha:透明度,1是完全不透明,0是完全透明。
属性动画系统是一个允许你做几乎任何事情的强大的框架。你可以定义一个在一定时间内对象的属性不断变化的动画。属性动画的实现是在指定的时间内更改属性(一个对象中的字段)。想要执行什么动作,你就应该指定你希望动画变化的对象的指定属性。
属性动画有以下几个重要的属性:
- Duration(持续时间):你可以指定动画的持续时间。默认大小为300毫秒。
- Time interpolation(时间插值):时间插值函数. (指定属性值变化的快慢) 如:AccelerateDecelerateInterpolator。
- Repeat count and behavior(重复计数和行为):你可以指定当动画结束的时候是否重复该动画,重复多少次该动画。您还可以指定是否希望动画反向回放。设置为反向播放动画向前然后向后地重复,直到到达重复次数。
- Animator sets(动画设置):你可以把动画放在一个组内,可以同时执行多个动画效果。
- Frame refresh delay(帧刷新延迟):可以指定多长时间刷新你的动画帧。默认设置为每10毫秒刷新一次动画帧,但最终依赖系统的当前状态。
二.属性动画如何工作?
1.首先,我们看一个简单的例子。图1描述了一个假想的对象,动画和X属性(它在屏幕上的水平位置)。动画的持续时间设置为40毫秒,距离是40像素。默认的帧刷新率每10毫秒刷新一次,物体水平移动10像素。40ms后结束,动画停止,物体水平移动了40个像素。这是一个带线性插值的动画例子,所展现的是一个以恒定的速度运动的目标。
图1
2.上面的例子是一个线性插值动画的例子,你也可以指定有一个非线性插值动画。图2说明了一个假设的对象,在动画开始时加速,结束时减速的动画。对象还是在40毫秒移动40像素,但非线性。在开始的时候,这个动画加速到中途点,然后从中途点减速直到动画结束。如图2所示,在开始和结束的动画距离是小于在中间时的。
图2
3.让我们来详细看看属性动画系统的重要组成部分,图3描述了主要的类如何与另一个工作。
图3
ValueAnimator本身不作用于任何对象,也就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后我们可以监听其动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的对象做了动画。
ValueAnimator封装timeinterpolator,定义动画插值和TypeEvaluator(可以理解为估值器),它定义了如何计算被修改了的组件的属性的值。
当想要开始一个动画的时候,应该先创建一个ValueAnimator并给它开始和结束值的属性值,你想动画做什么动作,以及动画的持续时间。当你调用start()动画开始。整个动画中,ValueAnimator会基于动画的持续时间和经过了多少时间计算出一个0和1之间分数。用分数表示动画已经完成了的时间的百分比,0就是0%,1就是100%。例如,在图1中,在t = 10毫秒时,该值应该是0.25,因为总时间t = 40毫秒。
当ValueAnimator完成计算一个逝去分数(当前执行时间占动画总执行时间的比例),它调用当前设置的TimeInterpolator,计算插值分数。一个被插值分数映射逝去分数到一个新的分数,考虑了被设置的时间插值。例如,在图2中,因为动画慢慢加速,插值后的分数0.15小于逝去分数0.25,如图1,在t = 10 ms,插值分数等于逝去分数。
当插值分数被计算的时候,ValueAnimator调用合适的TypeEvaluator,基于插值分数的初始值以及动画的结束值动态的计算该属性的值。例如,在图2中,在t = 10毫秒时分数为0.15,所以实际的属性值将是0.15 *(40 - 0)=6。
三.属性动画与View 动画不同
View动画系统只对动画视图对象有用,所以如果你想非视图对象动画,就需要实现你自己的代码来做。视图动画系统也受到限制,只公开了几方面的动画视图对象,,如一个视图的缩放和旋转而无法修改背景色。
视图动画系统的另一个缺点是,它只在视图被修改,而不是实际的视图。例如,如果你的动画按钮在屏幕上移动,按钮绘制正确,但实际的位置,你可以点击按钮并没有改变,所以你要实现你自己的逻辑来处理这个。
使用属性动画系统,这些约束完全可以解除,你可以对任何对象的任何属性进行操作。属性动画系统在一个较高的水平以更稳健的方式进行动画。指定你想要操作的动画属性,如颜色,大小,位置等等。
三.API 概述
你可以在在android.animation属性动画系统找到大部分的API。因为视图动画系统已经在android.view.animation定义了多种插值算法,你可以直接使用这些封装好的动画效果。
Animator类提供了用于创建动画的基本结构。你通常不直接使用这个类,它只提供最低限度的功能,通常根据自己的需要对该类进行扩展。下面介绍一下Animator的子类:
ValueAnimator
属性动画,它的所有核心功能是计算动画值和每个动画的时间细节,关于一个动画是否有重复的信息,监听并接收更新事件,并且能够自定义动画。属性动画有两个步骤:计算属性的值和设定属性的值。ValueAnimator只进行计算,所以你必须使用ValueAnimator计算值不断的来设置更新该对象的值。ObjectAnimator
ObjectAnimator是ValueAnimator的子类,它允许你设定一个目标对象和对象的属性动画。当它为动画计算新值时,就可以进行相应地属性更新。- AnimatorSet
AnimatorSet提供了一种机制,可以把多个动画放在一个群组里一起执行,可以实现更多的动画效果。你可以设置一个顺序动画(也可以指定动画的延迟)。
Evaluators告诉属性动画系统如何计算属性值。他们是由动画类提供的时序数据,动画的开始和结束值,在这个数据基础计算动画实际的值。属性动画系统提供以下evaluators:
- IntEvaluator
计算默认属性为int类型的值。 - FloatEvaluator
计算默认属性为float类型的值。 - ArgbEvaluator
计算表示为十六进制的颜色属性的值。 - TypeEvaluator
自定义计算方式的一个接口,如果你想要的动画的对象属性是不是int,float或颜色,你必须实现的TypeEvaluator接口指定如何计算对象属性值的动画。如果你想处理这些类型不同的默认行为,你也可以自定义的TypeEvaluator,指定一个int,float和颜色的值。
时间插补器time interpolator定义了一个作为时间的函数计算的动画中的特定值。例如,您可以指定在整个动画过程中线性发生动画,意味着动画的动作均匀的整个时间,或者你可以指定要使用的非线性时间,例如:结束时加速和开始时减速的动画。下面介绍一下android.view.animation中包含的插值器。如果没有提供你需要的插值器,为了满足需求,可以通过实现timeinterpolator接口并创建你自己的插值器。
1、 AccelerateDecelerateInterpolator
插值器的变化率开始和结束缓慢而中间加速通过。
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationX(f); } });
2、 AccelerateInterpolator
插值器的变化率开始缓慢,然后加快。
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new AccelerateInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationX(f); } });
3、 AnticipateInterpolator
先回退一小步,然后再迅速前进
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new AnticipateInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationX(f); } }); animator.start();
4、 AnticipateOvershootInterpolator
先回退一小步,然后再迅速前进,在超过右边界一小步
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new AnticipateOvershootInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationX(f); } }); animator.start();
5、 BounceInterpolator
实现弹球效果
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new BounceInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationY(f); } }); animator.start();
6、 CycleInterpolator
周期运动
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,300f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new CycleInterpolator(3) { @Override public float getInterpolation(float input) { return super.getInterpolation(input); } }); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationY(f); } }); animator.start();
7、 DecelerateInterpolator
减速
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new DecelerateInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationY(f); } }); animator.start();
8、 LinearInterpolator
匀速
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new LinearInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationY(f); } }); animator.start();
9、 OvershootInterpolator
快速前进到右边界上,再往外突出一小步
- 效果图
- 测试代码
//构建animator对象 animator = ValueAnimator.ofFloat(50f,600f); //设置缩放时间 animator.setDuration(2000); animator.setInterpolator(new OvershootInterpolator()); animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationY(f); } }); animator.start();
10、 TimeInterpolator
允许你实现你自己的插值器的接口。
四.ValueAnimator动画使用方式
ValueAnimator,是通过修改属性由一个值变化为另外一个值,然后根据值的变化,按照一定的规则,动态修改View的属性,比如View的位置、透明度、旋转角度、大小等。
ValueAnimator类允许指定一组int,float或色彩值,通过调用它的一个工厂方法:ofint(),offloat()或ofobject()得到一个ValueAnimator对象。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);animation.setDuration(1000);animation.start();
在这段代码中,ValueAnimator开始计算动画的值,0和1之间,持续时间为1000毫秒,这start()方法运行时。
你也可以指定一个自定义类型的动画做以下:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);animation.setDuration(1000);animation.start();
在这段代码中,ValueAnimator通过一个自定义动画类MyTypeEvaluator来实现在1000ms内完成属性值startPropertyValue到endPropertyValue的转变,调用start()方法开始执行。
然而,前面的代码片段对一个对象并没有实际效果,因为ValueAnimator并不直接操作的对象或属性。你现在最想做的事是通过这些计算值来修改对象的属性。你可以通过定义监听器在ValueAnimator动画寿命期间的重要事件时进行适当处理,如帧更新。在监听过程中,你可以通过调用getanimatedvalue()刷新特定帧的计算值下面通过几个例子来展示实际的应用过程。
例子1:缩放imageview从初始大小1f到0f
1-1.通过代码来实现,activity_main.xml布局如下
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" tools:context="${relativePackage}.${activityClass}" > <ImageView android:id="@+id/image" android:layout_width="280dp" android:layout_height="260dp" android:src="@drawable/image" /></RelativeLayout>
1-2.java代码操作image控件从1f缩放到0f
public class MainActivity extends Activity{ private ImageView image; private ValueAnimator animator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); image = (ImageView)findViewById(R.id.image); image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { animator.start(); } }); startValueAnimator(); } private void startValueAnimator() { //构建animator对象,执行缩放值1f-0f animator = ValueAnimator.ofFloat(1f,0f); //设置缩放时间 animator.setDuration(1000); //更新界面 animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //获取经过计算得到的单个属性的值,此处是属性scaleX,scaleY的值 float f = (float)animation.getAnimatedValue(); image.setScaleX(f); image.setScaleY(f); } }); }}
1-3.运行效果
1-4.xml动画文件实现例1效果
- 在res目录下新建animator目录,在animator目录下新建animator_scale.xml文件
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" > <objectAnimator android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="0.0" android:valueType="floatType" /> <objectAnimator android:duration="1000" android:propertyName="scaleY" android:valueFrom="1.0" android:valueTo="0.0" android:valueType="floatType" /></set>
- 代码中使用,即可实现例1效果
xmlAnimator = AnimatorInflater.loadAnimator(MainActivity.this, R.animator.animator_scale);xmlAnimator.setTarget(image);xmlAnimator.start();
例子2:缩放+旋转+平移+渐变消失效果
2-1.具体实现代码
animatorSet = new AnimatorSet(); //平移 pyAnimator = ValueAnimator.ofFloat(50f,600f); //缩放 sfAnimator = ValueAnimator.ofFloat(1f,0f); //旋转 xzAnimator = ValueAnimator.ofFloat(0f,180f); //渐变 jbAnimator = ValueAnimator.ofFloat(1f,0f); animatorSet.playTogether(pyAnimator,sfAnimator,xzAnimator,jbAnimator); //设置缩放时间 animatorSet.setDuration(2000); pyAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setTranslationX(f); } }); sfAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setScaleX(f); image.setScaleY(f); } }); xzAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setRotation(f); } }); jbAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float f = (float)animation.getAnimatedValue(); image.setAlpha(f); } });
2-2.开启动画
animatorSet.start();
2-3.执行效果图
五. objectanimator动画详解
objectanimator是ValueAnimator的子类,结合时序引擎和动画命名一个目标对象的属性的能力,ValueAnimator值计算。这使得动画物体容易得多,因为你不再需要实现valueanimator.animatorupdatelistener,因为动画属性的自动更新。
objectanimator的使用和ValueAnimator非常类似,但也必须随着动画之间的值来指定对象和对象的属性的名称(字符串):
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);anim.setDuration(1000);anim.start();
为了确保objectanimator更新属性正确,你必须做到以下几点:
- 设置的动画的对象属性必须有一个setter函数。因为objectanimator自动更新的动画,它必须能够通过这个setter方法访问属性。例如,如果属性名为foo,你需要有一个setfoo()方法。如果这个setter方法是不存在的,你有三个选择:
1、如果你有这样做的权利,添加setter方法。
2、使用包装类,你必须改变和有包装获得的值与一个有效的setter方法,提出了它原来的对象权限。
而不是使用ValueAnimator。 - 如果你指定的属性变化参数(…参数)值只有一个值,它被认为是动画的结束值。因此,在你的动画对象的属性必须有一个getter函数用于获取动画的起始值。例如,如果属性名为foo,你需要有一个getfoo()方法。
- 你指定的当前动画的属性变化值必须是同一个类型,例如,你指定了float的类型的开始值,就必须确保结束值也是float。
- 在对某些属性进行动画操作的时候,你可能需要调用invalidate()方法使屏幕重绘自身,在onanimationupdate()回调方法中可以完成这些。例如,如果是设置color属性的动画,Drawable对象当界面重新绘制的时候才会更新。setalpha()和settranslationx()是会自动更新的,因此不需要调用invalidate()方法。
下面通过几个例子展示一下objectAnimator基本用法:
例子1:缩放,旋转,平移,透明度动画
1-1.添加布局文件activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="${relativePackage}.${activityClass}" > <ImageView android:id="@+id/image" android:layout_width="80dp" android:layout_height="80dp" android:src="@drawable/dph" /> <Button android:id="@+id/btn_translate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="平移" /> <Button android:id="@+id/btn_rotate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_toRightOf="@+id/btn_translate" android:text="旋转" /> <Button android:id="@+id/btn_scale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/btn_translate" android:text="缩放" /> <Button android:id="@+id/btn_alpha" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/btn_rotate" android:layout_toRightOf="@+id/btn_scale" android:text="透明度" /></RelativeLayout>
1-2.代码实现
//imageview x方向从50平移到500的位置ObjectAnimator animator = ObjectAnimator.ofFloat(image, "translationX", 50,500); animator.setDuration(2000); animator.start();//imageview旋转:0度-360度-0度ObjectAnimator animator = ObjectAnimator.ofFloat(image, "rotation", 0,360,0); animator.setDuration(2000); animator.start();//x,y同时缩放1f-0f-1f(从初始状态到消失再还原)AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator animator = ObjectAnimator.ofFloat(image, "scaleX", 1f,0f,1f); ObjectAnimator animatorY = ObjectAnimator.ofFloat(image, "scaleY", 1f,0f,1f); animatorSet.playTogether(animator,animatorY); animatorSet.setDuration(2000); animatorSet.start();//透明度从不透明到全透明,再到不透明ObjectAnimator animator = ObjectAnimator.ofFloat(image, "alpha", 1f,0f,1f); animator.setDuration(2000); animator.start();
1-3.效果图
例子2:往前滚动的小球
2-1.例1的代码稍微改变一下即可实现,代码如下:
AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator animatorA = ObjectAnimator.ofFloat(image, "translationX", 50,600); ObjectAnimator animatorB = ObjectAnimator.ofFloat(image, "rotation", 0,720); animatorSet.playTogether(animatorA,animatorB); animatorSet.setDuration(1000); animatorSet.start();
2-2.效果图
六.使用Animatorset编排多个动画
在许多情况下,你想实现一个动画,取决于一个动画的开始或完成。Android系统允许你捆在一起形成一个animatorset动画,使您可以指定是否同时启动动画,顺序启动动画或在指定的延迟后启动动画。你也可以在animatorset中嵌套animatorset对象。使用起来非常简单,其实上面的例子已经有使用到。
下面通过一个例子来演示一下animatorset的使用:
1.直接上代码:
//旋转动画animatorRotate ObjectAnimator animatorRotate = ObjectAnimator.ofFloat(image, "rotation", 0,360,0); animatorRotate.setDuration(1000); //平移动画animatorTrans ObjectAnimator animatorTrans = ObjectAnimator.ofFloat(image, "translationX", 50,500); animatorTrans.setDuration(2000); //缩放动画animatorSet AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator animator = ObjectAnimator.ofFloat(image, "scaleX", 1f,0f,1f); ObjectAnimator animatorY = ObjectAnimator.ofFloat(image, "scaleY", 1f,0f,1f); animatorSet.playTogether(animator,animatorY); animatorSet.setDuration(2000); //透明度动画animatorAlpha ObjectAnimator animatorAlpha = ObjectAnimator.ofFloat(image, "alpha", 1f,0f,1f); animatorAlpha.setDuration(2000); //动画集合bouncer AnimatorSet bouncer = new AnimatorSet(); //旋转animatorRotate之前先执行平移animatorTrans //animatorTrans的同时animatorSet(缩放) //平移+缩放完成后执行animatorRotate旋转 //最后执行fadeAnim,消失bouncer.play(animatorTrans).before(animatorRotate); bouncer.play(animatorTrans).with(animatorSet); bouncer.play(animatorTrans).after(animatorAlpha); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(image, "alpha", 1f, 0f); fadeAnim.setDuration(1000); AnimatorSet animatorMySet = new AnimatorSet(); //执行fadeAnim之前先执行bouncer动画 animatorMySet.play(bouncer).before(fadeAnim); animatorMySet.start();
2.运行效果
关于属性动画的基础知识先说到这,后续会介绍动画的监听,布局动画和TypeEvaluator,ViewPropertyAnimator等等,敬请期待。