Android属性动画
1. 什么是Android属性动画?
Android动画分为视图动画(View 动画)、帧动画(Frame 动画、Drawable 动画)、属性动画、触摸反馈动画(Ripple Effect)、揭露动画(Reveal Effect)、转场动画与共享元素(Activity 切换动画)、视图状态动画(Animate View State Changes)、矢量图动画(Vector 动画)、约束布局实现的关键帧动画(Constraint Set 动画)等等。属性动画知识android动画的一种而已,它可以将动画添加到所有提供了Getter和Setter方法的对象的属性上,其实现代码更加简洁,不过上手难度较高,本文只做一个抛砖引玉。
2. Android属性动画的使用场景
- 第一个场景也是最普遍的场景,只要是能够作用在View上的动画效果,使用属性动画都可以实现;
- 我们想对某一个视图的不同属性实现不同的动画效果时;
- 我们想监听动画进行过程中一些属性值并根据属性值的不同做不同的处理时;
总之它的使用场景是有很多的,这里就不一一列举了。
3. 属性动画核心知识
①对象动画(ObjectAnimator)
关于ObjectAnimator对象的获取这里只贴一个,其他的大家感兴趣可以自己去看
/**
* @param target 添加动画的目标对象
* @param propertyName 正在动画的属性的名称
* @param values 随时间推移不断变化的一组值
* @return 返回在给定值之间设置动画的一个动画对象
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
我们再看一下new ObjectAnimator(target, propertyName)干了 什么
/**
*
* @param target 添加动画的目标对象
* @param propertyName 正在动画的属性的名称
*/
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
@Override
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference<Object>(target);
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
new ObjectAnimator(target, propertyName)为ObjectAnimator对象设置了动画的目标对象以及动画的属性的名称,这个名称可以是alpha、translationX、translationY、scaleX、scaleY等等。最后就是将我们给定的一组值设置给ObjectAnimator对象:
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
ObjectAnimator对象获取到了以后我们只需要再为其设置动画的时长、动画的重复次数、估值器、插值器、动画的延迟等属性,然后再调用其start()方法开启动画即可。
②值动画(ValueAnimator)
值动画其实也是对象动画的父类,对象动画是直接将添加动画的目标对象给定了,而值动画是直接给定一组值,至于如何变、变谁我们可以为其设置动画更新监听器,根据动画值的变化为视图添加相应的变化。获取ValueAnimator对象,这里也只贴一个:
public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
return anim;
}
public void setFloatValues(float... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofFloat("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setFloatValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
这里是直接创建了ValueAnimator对象,并为其设置了给定的一组值,具体变谁、怎么变我们要为其设置动画更新监听器,然后来看一下动画监听器接口:
public static interface AnimatorUpdateListener {
/**
* @param animation 重复的动画
*/
void onAnimationUpdate(ValueAnimator animation);
}
实现该接口值需要onAnimationUpdate()方法,在该方法中去实现各种变换的逻辑,动画进行中的AnimatedValue可以通过调用传入的ValueAnimator对象的getAnimatedValue()方法进行获取。同样的,获取到ValueAnimator对象以后我们可以为其设置动画时长、动画重复次数、动画重复模式等等。
③PropertyValueHolder
PropertyValueHolder对象的获取与上面两个动画对象的获取是一样的,同样是调用其静态方法ofFloat()、ofMultiInt()、ofInt()等等进行获取,不同的是我们可以获取很多不同变换的PropertyValueHolder对象,然后调用ObjectAnimator类的ofPropertyValuesHolder方法将这些PropertyValueHolder对象都设置给同一个目标对象。
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(target);
anim.setValues(values);
return anim;
}
该方法返回的是一个ValueAnimator对象,拿到这个对象再为其设置动画时长、动画重复的次数、动画重复的模式等等,最后调用其start()方法开启动画即可。
④AnimatorSet
AnimatorSet也可以实现多个动画同时进行,不同的是AnimatorSet还可以控制动画执行的顺序:
//动画1,2同时执行
animatorSet.play(animator1).with(animator2);
//动画2执行完成后执行动画3
animatorSet.play(animator3).after(animator2);
//动画1,2,3按顺序执行
animatorSet.playSequentially(animator1,animator2,animator3);
//动画1,2,3同时执行
animatorSet.playTogether(animator1,animator2,animator3);
设置完动画的执行顺序以后再调用AnimatorSet对象的start()方法开启动画即可。
⑤估值器(TypeEvaluator)
TypeEvaluator是一个接口:
public interface TypeEvaluator<T> {
/**
* 他的函数返回线性插值开始值和结束值的结果
* @param fraction 从开始值到结束值的分数
* @param startValue 开始值
* @param endValue 结束值
* @return 给定的开始值和结束值之间的线性插值
*/
public T evaluate(float fraction, T startValue, T endValue);
}
该接口只有一个需要实现的方法evaluate(),在该方法中去设置动画进行过程中的变化值,为动画设置了估值器以后,然后我们再为动画设置动画更新监听器,在动画更新监听器中通过传入的ValueAnimator对象进行获取动画进行中的AnimatedValue,然后根据获取到的AnimatedValue为视图设置我们想要的变换。
⑥插值器(Interpolator )
官方已经为我们定义好了9中内置插值器:
效果 | 资源ID | JAVA类 |
---|---|---|
加速 | @android:anim/accelerate_interpolator | AccelerateInterpolator |
快速完成动画,超出再回到结束样式 | @android:anim/overshoot_interpolator | OvershootInterpolator |
先加速再减速 | @android:anim/accelerate_decelerate_interpolator | AccelerateDecelerateInterpolator |
先退后再加速前进 | @android:anim/anticipate_interpolator | AnticipateInterpolator |
先退后再加速前进,超出终点后再回终点 | @android:anim/anticipate_overshoot_interpolator | AnticipateOvershootInterpolator |
最后阶段弹球效果 | @android:anim/bounce_interpolator | BounceInterpolator |
周期运动 | @android:anim/cycle_interpolator | CycleInterpolator |
减速 | @android:anim/decelerate_interpolator | DecelerateInterpolator |
匀速 | @android:anim/linear_interpolator | LinearInterpolator |
使用时:
- 当在XML文件设置插值器时,只需传入对应的插值器资源ID即可
- 当在Java代码设置插值器时,只需创建对应的插值器对象即可
系统默认的插值器是AccelerateDecelerateInterpolator,即先加速后减速
系统内置的9种插值器可以实现大多数的动画需求,当然我们也就可以自定义插值器,我们先看一下Interpolator接口和TimeInterpolator接口:
/**
* 插值器定义动画的变化率。这允许基本的动画效果(alpha,缩放,平移,旋转)、加速、减速、重复等。
*/
public interface Interpolator extends TimeInterpolator {
//为新的android.animation引入了一个新的接口TimeInterpolator包。这个较旧的插入器接口扩展了TimeInterpolator,使用户新的基于动画器的动画可以使用旧的插值器实现或直接实现TimeInterpolator的新类。
}
/**
* 时间插值器定义了动画的变化率。这允许动画有非线性运动,如加速和减速。
*/
public interface TimeInterpolator {
/**
* 将表示动画经过部分的值映射为表示的值内插分数。这个内插的值乘以动画的值,以派生当前经过的动画时间的动画值。
*
* @param input 一个介于0和1.0之间的值,指示当前点在动画中0代表开始,1.0代表结束
* @return 插补值。此值可以大于1.0超过其目标的插值器,或小于0的没有达到目标的插值器。
*/
float getInterpolation(float input);
}
自定义插值器时,如果是补间动画就实现Interpolator接口,如果是属性动画就实现TimeInterpolator接口,然后实现getInterpolation()方法,在该方法中根据传入的input值去做我们需要的各种处理。
4. 总结
通过本文的学习应该只能让你对属性动画的基本使用有一个简单的认识,要想熟练的应用属性动画还得自己动手将基本的各种属性动画实现实现,多敲几遍你就能够掌握它的基本使用了。属性动画的上手难度相对来说会高一点,学习过程中相比其他知识点需要花费的事件也会多一点,总之还是多实践,经验有时候真的能让我们少走很多弯路,有些坑不踩一次永远都不会知道原来这里也有坑。
参考资料