概述
Android属性动画是Android 3.0之后添加的一个非常强大的动画。与视图动画不同的地方在于,属性动画能够真正的改变View的属性。例如:如果我们通过视图动画移动了一个View,那么这个View真正的位置并没有发生改变,它的点击事件依然保留在原来的位置。而如果我们通过属性动画改变了一个View的位置,那么它真实的位置也就改变了。此外属性动画并不局限于对View实现动画,它几乎可以对任何一个对象的任何属性实现动画。
Animator三个子类
通常我们实现属性动画并不需要直接使用Animator
,而是使用它的以下三个子类:
- ObjectAnimator
- ValueAnimator
- AnimatorSet
ObjectAnimator
一般我通过ObjectAnimator的工厂方法ofFloat()
来获得它的实例(其他的方法还有ofInt()
和ofArgb()
)。
例如下代码实现了一个位移动画:
ObjectAnimator translate = ObjectAnimator.ofFloat(view, "translationX", 200f);
translate.setDuration(1000);
translate.start();
ofFloat()
参数说明
- 第一参数是一个需要实现动画的View。
- 第二个参数是需要操作的属性。
- 最后一个其实是可变参数。用来表示属性值的一系列取值。当只传一个值的时候,表示从初始值,变化到该值。
常用属性值
-
translationX
和translationY
: 控制View距离左边和顶部的距离的增加值。是一个相对值。 -
rotation
、rotationX
和rotationY
:rotation
是控制View围绕其支点进行旋转。rotationX
和rotationY
分别是围绕X轴和Y轴旋转。 -
scaleX
和scaleY
: 控制View的缩放。 -
pivotX
和pivotY
: 控制View的支点位置,进行旋转和缩放,默认是View的中点。它们都是float
值,0
表示View的最左边和最顶端,1
表示最右端和最下端。 -
alpha
: 控制View的透明度。 -
x
和y
: 控制View在布局容器中距离左边和顶部的距离。是一个绝对值。
注意
当用属性动画操作一个View的属性时,这个View必须具有相应的get和set方法。
ValueAnimator
ValueAnimator
是ObjectAnimator
的父类。它有点像一个数值发生器。不提供任何动画效果。通常我们需要在onAnimationUpdate
获取当前时间点发生的值,然后操作对象的属性,以实现动画效果。
如下代码实现了一个倒计时效果:
ValueAnimator countDown = ValueAnimator.ofInt(10, 0);
countDown.setDuration(10000);
countDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
textView.setText("" + animation.getAnimatedValue());
}
});
countDown.start();
补充
系统大约每10ms刷新一次,来更新属性值。所以如果我们要做一个以秒为单位的计时器,可以直接用这种效果实现。
AnimatorSet
如果我们要对一个对象的多个属性实现动画,通常有如下两种做法:
- 使用PropertyValuesHolder
- 使用AnimatorSet
使用PropertyValuesHolder
PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.0f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.0f);
ObjectAnimator together = ObjectAnimator.ofPropertyValuesHolder(view, rotate, scaleX, scaleY);
together.setDuration(1000);
together.start();
使用AnimatorSet
ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f).setDuration(1000);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f).setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, 2.0f).setDuration(1000);
AnimatorSet set = new AnimatorSet();
set.playSequentially(rotate, scaleX, scaleY);
set.setDuration(1000);
set.start();
补充
AnimatorSet可以通过playSequentially(),playTogether(),来控制动画的串行和并行。通过set.play().with()、before()、after()等方法可以实现动画的多种协同效果。
操作一个没有set和get的属性
上文已经提到当用属性动画操作一个View的属性时,这个View必须具有相应的get和set方法。那么如果我们想操作一个没有get和set的属性,该怎么办呢?
通常有三种实现方式:
- 自己创建一个属性类。
- 通过一个包装类来实现。
- 通过ValueAnimator来实现。
通过包装类实现
当我们查看View的set和get方法时,它只有scaleX和scaleY,而没有scale属性。有时候,我们需要对一个View的X和Y都进行缩放,又不想使用两次ObjectAnimator,就可以通过如下方法实现。
如下代码是一个包装类的示例:
public class WrapperView {
private View target;
private float scale;
public WrapperView(View target){
this.target = target;
}
public setScale(float scale){
this.scale = scale;
target.setScaleX = scale;
target.setScaleY = scale;
}
public float getScale(){
return scale;
}
}
使用该包装类直接实现对View的缩放:
WrapperView target = new WrapperView(view);
ObjectAnimator scale = ObjectAnimator.ofFloat(target,"scale",1f,2f);
scale.setDuration(1000);
scale.start();
通过ValueAnimator实现
如果我们想实现一个View在一个曲线运动的动画,就可以通过如下方法实现。
(曲线公式为:y=-1/150x^2+4x )
public static final int TRANS_X = 600;
ValueAnimator animator = new ValueAnimator();
float offsetX = view.getX();
float offsetY = view.getY();
animator.setDuration(2000);
// 和ofFloat类似,设置属性的起始值和结束值。
animator.setObjectValues(new PointF(offsetX, offsetY), new PointF(TRANS_X + offsetX, offsetY));
// 设置自定义的Evaluator.
animator.setEvaluator(new TypeEvaluator() {
@Override
// api level 21以上已经实现了PointFEvalutor.
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF = new PointF();
float d = fraction * TRANS_X;
pointF.x = startValue.x + d;
pointF.y = startValue.y + (1.0f / 150f) * d * d - 4 * d;
return pointF;
}
});
// 通过监听器得到相应的Evaluator值,并应用。
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
view.setX(pointF.x);
view.setY(pointF.y);
}
});
animator.start();
说明
通常如果我们需要操作一个不存在的属性,我们需要自定义一个Evaluator
。通过这个Evaluator
在动画运行的过程中生成相应的值。然后给ValueAnimator
设置一个监听器,通过getAnimatedValue()
来得到相应的值。然后根据这个值,做相应的操作。
关于Evaluator
Evaluator
翻译过来是估值器的意思,实际上它是一种计算属性值的算法。
- 系统定义的
Evaluator
有ArgbEvaluator, FloatEvaluator, IntEvaluator, PointFEvaluator等。 - 我们自定义
Evaluator
只需要实现TypeEvaluator
接口,并重写T evaluate(float fraction,T startValue,T endValue)
方法即可。其中fraction
是根据设置的Interpolator
生成的(系统默认的Interpolator是AccelerateDecelerateInterpolator
,即先加速后减速。),它表示变化的百分比(0表示0%,1表示100%)。例如:如果我们将一个View从(0,0)移动到(0,100),duration
是1000ms,Interpolator为LinearInterpolator
。那么在动画执行到500ms时,这个fraction
就等于0.5。
在xml中使用属性动画
属性动画的保存在res/animator文件夹下。
如下代码定义了一个AnimatorSet:
xml文件
在Java代码中调用
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator);
animator.setTarget(view);
animator.start();
通过KeyFrame定义动画
// 这里ofFloat(),的第一个参数表示动画完成的百分比。
Keyframe keyframe1 = Keyframe.ofFloat(0f, 0f);
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 360f);
Keyframe keyframe3 = Keyframe.ofFloat(1.0f, 0f);
PropertyValuesHolder pvh = PropertyValuesHolder.ofKeyframe("rotation", keyframe1, keyframe2, keyframe3);
ObjectAnimator rotate = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
rotate.setDuration(2000);
rotate.start();
动画事件的监听
我们可以使用AnimatorListen
接口来监听动画的Start、Repeat、End、Cancel。但是这样做会过于繁琐,有时候我们只想监听动画的部分事件,这时候我们可以使用AnimatorListenAdapter
接口。
直接使用View的animate方法
view.animate()
.alpha(0)
.y(300)
.setDuration(1000)
.withStartAction(new Runnable(){
@Override
public void run(){
}
})
.withEndAction(new Runnable(){
@Override
public void run(){
runOnUiThread(new Runnable(){
@Override
public void run(){
}
});
}
}).start();
注: 本文主要参考有:网易微专业课程、Android群英传。当然还有Api guide。