上一篇【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是完全透明。
属性动画系统是一个允许你做几乎任何事情的强大的框架。你可以定义一个在一定时间内对象的属性不断变化的动画。属性动画的实现是在指定的时间内更改属性(一个对象中的字段)。想要执行什么动作,你就应该指定你希望动画变化的对象的指定属性。
属性动画有以下几个重要的属性:
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动画系统只对动画视图对象有用,所以如果你想非视图对象动画,就需要实现你自己的代码来做。视图动画系统也受到限制,只公开了几方面的动画视图对象,,如一个视图的缩放和旋转而无法修改背景色。
视图动画系统的另一个缺点是,它只在视图被修改,而不是实际的视图。例如,如果你的动画按钮在屏幕上移动,按钮绘制正确,但实际的位置,你可以点击按钮并没有改变,所以你要实现你自己的逻辑来处理这个。
使用属性动画系统,这些约束完全可以解除,你可以对任何对象的任何属性进行操作。属性动画系统在一个较高的水平以更稳健的方式进行动画。指定你想要操作的动画属性,如颜色,大小,位置等等。
你可以在在android.animation属性动画系统找到大部分的API。因为视图动画系统已经在android.view.animation定义了多种插值算法,你可以直接使用这些封装好的动画效果。
Animator类提供了用于创建动画的基本结构。你通常不直接使用这个类,它只提供最低限度的功能,通常根据自己的需要对该类进行扩展。下面介绍一下Animator的子类:
ValueAnimator
属性动画,它的所有核心功能是计算动画值和每个动画的时间细节,关于一个动画是否有重复的信息,监听并接收更新事件,并且能够自定义动画。属性动画有两个步骤:计算属性的值和设定属性的值。ValueAnimator只进行计算,所以你必须使用ValueAnimator计算值不断的来设置更新该对象的值。
ObjectAnimator
ObjectAnimator是ValueAnimator的子类,它允许你设定一个目标对象和对象的属性动画。当它为动画计算新值时,就可以进行相应地属性更新。
Evaluators告诉属性动画系统如何计算属性值。他们是由动画类提供的时序数据,动画的开始和结束值,在这个数据基础计算动画实际的值。属性动画系统提供以下evaluators:
时间插补器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,是通过修改属性由一个值变化为另外一个值,然后根据值的变化,按照一定的规则,动态修改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效果
<?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>
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是ValueAnimator的子类,结合时序引擎和动画命名一个目标对象的属性的能力,ValueAnimator值计算。这使得动画物体容易得多,因为你不再需要实现valueanimator.animatorupdatelistener,因为动画属性的自动更新。
objectanimator的使用和ValueAnimator非常类似,但也必须随着动画之间的值来指定对象和对象的属性的名称(字符串):
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
为了确保objectanimator更新属性正确,你必须做到以下几点:
下面通过几个例子展示一下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.效果图
在许多情况下,你想实现一个动画,取决于一个动画的开始或完成。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等等,敬请期待。