您的浏览器不支持CSS3,建议使用Firfox、Chrome等浏览器,以取得最佳显示效果

Android Animation完全总结(三)Animator与LayoutTransition

Android 477℃ 0 1年前 (2016-06-03)

示例代码、思维导图可在此下载
https://github.com/jzj1993/AndroidAnimation

Animator (属性动画 / Property Animation) (Android 3.0+)

Animation 与 Animator 对比

  • 原理:Animation通过改变View的Matrix,不断重绘实现动画,Animator则直接调用Object的setter方法
  • 前者只改变界面显示,即使View的显示位置发生变化,点击事件还是发生在原来的地方
  • 前者只能作用于View,后者可以作用于任意Object
  • 前者只能改变View的显示效果,后者可以平滑改变Object的任意属性
  • 前者的执行效率相对较高,后者需要使用反射,效率较低
  • 后者在Android 3.0+版本中才能使用

相关的类和继承关系

  • 定义了新的TimeInterpolator插值器,为了兼容性,原有的Interpolator继承自TimeInterpolator。

ValueAnimator

ValueAnimator可以生成一个渐变的数值。例如使用ValueAnimator可以实现类似支付宝中账户余额渐变的动画效果。

使用示例

  1. ValueAnimator anim = ValueAnimator.ofInt(1, 100);
  2. anim.setDuration(3000);
  3. anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  4. @Override
  5. public void onAnimationUpdate(ValueAnimator animation) {
  6. Integer val = (Integer) animation.getAnimatedValue();
  7. mTextView.setText(String.valueOf(val));
  8. }
  9. });
  10. anim.addListener(new Animator.AnimatorListener() {
  11. @Override
  12. public void onAnimationStart(Animator animation) {
  13. }
  14. @Override
  15. public void onAnimationEnd(Animator animation) {
  16. mTextView.setText("HelloWorld");
  17. }
  18. @Override
  19. public void onAnimationCancel(Animator animation) {
  20. }
  21. @Override
  22. public void onAnimationRepeat(Animator animation) {
  23. }
  24. });
  25. // API Level 19 (Android 4.4) 才可以使用
  26. anim.addPauseListener(new Animator.AnimatorPauseListener() {
  27. @Override
  28. public void onAnimationPause(Animator animation) {
  29. }
  30. @Override
  31. public void onAnimationResume(Animator animation) {
  32. }
  33. });
  34. anim.start();

实例化

ValueAnimator有四个静态方法可用于实例化:

  1. public static ValueAnimator ofInt(int... values);
  2. public static ValueAnimator ofFloat(float... values);
  3. public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values);
  4. public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);
  • 其中前两个方法可以根据设定的起始值和终止值生成int、float序列。

  • 第三个方法用于从PropertyValuesHolder获取数据生成动画,后面介绍。

  • ofObject方法配合自定义的TypeEvaluator,可以计算任意类型的数据序列。

Listener

AnimatorListener, AnimatorPauseListener, AnimatorUpdateListener

TypeEvaluator

ValueAnimator默认只能产生int和float型数据动画序列。而使用自定义TypeEvaluator可以创建任意类型的动画序列。

  1. class MyTypeEvaluator implements TypeEvaluator<PointF> {
  2. /**
  3. * @param fraction 当前帧 0 ~ 1f
  4. * @param startValue 起始值
  5. * @param endValue 终止值
  6. * @return 当前帧的value插值计算结果
  7. */
  8. @Override
  9. public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
  10. return new PointF(
  11. startValue.x + fraction * (endValue.x - startValue.x),
  12. startValue.y + fraction * (endValue.y - startValue.y)
  13. );
  14. }
  15. }
  1. PointF start = new PointF(0, 0);
  2. PointF end = new PointF(mTextView.getX(), mTextView.getY());
  3. ValueAnimator anim = ValueAnimator.ofObject(new MyTypeEvaluator(), start, end);
  4. anim.setDuration(2000);
  5. anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  6. @Override
  7. public void onAnimationUpdate(ValueAnimator animation) {
  8. PointF val = (PointF) animation.getAnimatedValue();
  9. mTextView.setX(val.x);
  10. mTextView.setY(val.y);
  11. }
  12. });
  13. anim.start();

ObjectAnimator、AnimatorSet

ValueAnimator只能生成Value值的动画序列,而ObjectAnimator则可以指定Target(目标)和Property(属性),利用反射中的Setter,将Value序列设置给Target的Property。

AnimatorSet可以组合多个Animator,使其一起播放或按照指定的先后顺序播放。

用法示例

  1. Animator[] anim = new Animator[3];
  2. anim[0] = ObjectAnimator.ofFloat(mTextView, "alpha", 0, 1);
  3. anim[1] = ObjectAnimator.ofFloat(mTextView, "scaleX", 2f, 1f);
  4. anim[2] = ObjectAnimator.ofFloat(mTextView, "translationY", -300, 0);
  5. for (Animator a : anim) {
  6. a.setDuration(1000);
  7. }
  8. AnimatorSet set = new AnimatorSet();
  9. set.playTogether(anim);
  10. set.start();

方法ofFloat中的参数:

  • 第一个为Target,即要对哪个对象执行动画;
  • 第二个为属性,这里用的是字符串,执行动画时会用反射,自动调用字符串对应属性的setter;
  • 后面的参数,用于设置属性的值。

AnimatorSet

AnimatorSet.playTogether 方法,指定同时播放每个Animator动画。因为AnimatorSet继承自Animator,所以该方法的参数也可以为其他AnimatorSet

AnimatorSet.playSequentially方法,指定依次执行每个Animator动画。

  1. Animator[] anim = new Animator[4];
  2. anim[0] = ObjectAnimator.ofFloat(mTextView, "translationX", 0, 100);
  3. anim[1] = ObjectAnimator.ofFloat(mTextView, "translationY", 0, 100);
  4. anim[2] = ObjectAnimator.ofFloat(mTextView, "translationX", 100, 0);
  5. anim[3] = ObjectAnimator.ofFloat(mTextView, "translationY", 100, 0);
  6. for (Animator a : anim) {
  7. a.setDuration(600);
  8. }
  9. AnimatorSet set = new AnimatorSet();
  10. set.playSequentially(anim);
  11. set.start();

AnimatorSet.Builder

  1. Animator[] anim = new Animator[4];
  2. anim[0] = ObjectAnimator.ofFloat(mTextView, "translationX", 0, 100);
  3. anim[1] = ObjectAnimator.ofFloat(mTextView, "translationY", 0, 100);
  4. anim[2] = ObjectAnimator.ofFloat(mTextView, "translationX", 100, 0);
  5. anim[3] = ObjectAnimator.ofFloat(mTextView, "translationY", 100, 0);
  6. for (Animator a : anim) {
  7. a.setDuration(600);
  8. }
  9. AnimatorSet set1 = new AnimatorSet();
  10. AnimatorSet set2 = new AnimatorSet();
  11. set1.play(anim[0]).before(anim[1]);
  12. set1.play(anim[2]).after(anim[1]);
  13. set2.play(anim[3]).after(set1);
  14. set2.start();
  1. AnimatorSet.play方法,返回一个AnimatorSet.Builder实例,可以调用Builder的before、after、with,指定与其他动画之间的播放次序。

  2. AnimatorSet的播放可以嵌套。

注意:

  • AnimatorSet.play方法返回的是一个新的Builder实例
  • 而Builder的before、after、with方法返回的是这个Builder实例自身,要慎用Builder的连写方式。
  • 下面的代码表示anim[0]播放完后,同时播放anim[1]和anim[2],而不是依次播放三个动画。详见AnimatorSet.Builder的JavaDoc说明。

    set.play(anim[0]).before(anim[1]).before(anim[2]);

PropertyValuesHolder

前面利用AnimatorSet和多个Animator,可以实现让一个Target的多个属性同时动画的效果。而使用PropertyValuesHolder,只要用一个Animator即可实现同样的效果,且性能更好。

  • 每个PropertyValuesHolder包含一个Property和对应的Value
  • 一个ObjectAnimator可以包含多个PropertyValuesHolder
  1. PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 0, mTextView.getX());
  2. PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 0, mTextView.getY());
  3. PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat("alpha", 0, 1);
  4. ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTextView, pvhX, pvhY, pvhA);
  5. anim.setDuration(1000);
  6. anim.start();

KeyFrame

KeyFrame可以实现分段动画

  1. final float y = mTextView.getY();
  2. // fraction, value
  3. Keyframe kf[] = new Keyframe[]{
  4. Keyframe.ofFloat(0f, 0),
  5. Keyframe.ofFloat(0.2f, 0.4f * y),
  6. Keyframe.ofFloat(0.5f, 0.3f * y),
  7. Keyframe.ofFloat(0.8f, 0.8f * y),
  8. Keyframe.ofFloat(1f, y)
  9. };
  10. PropertyValuesHolder pvhK = PropertyValuesHolder.ofKeyframe("y", kf);
  11. ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTextView, pvhK);
  12. anim.setDuration(5000);
  13. anim.start();
  • 不使用KeyFrame时,随着fraction的均匀递增,value的值线性增长。配合LinearInterpolator可以实现View匀速运动。
  • 使用KeyFrame后,每两个关键帧之间成为一段动画,期间Value值线性变化。右图中每个点表示一个KeyFrame。
  • 使用KeyFrame时,也可以使用Interpolator。
  • 注意:使用KeyFrame时,随着时间推移,Value的值不仅可以递增,也可以递减。
  • 注意:即使使用的是LinearInterpolator,最后Value的值改变并不一定是均匀的,因为每两个KeyFrame之间连线的斜率不一样。

CustomTarget

ObjectAnimator执行时,是用反射设置Target属性。因此也可以自行定义任意Target。

  1. Object target = new CustomTarget(mTextView);
  2. ObjectAnimator anim = ObjectAnimator.ofFloat(target, "translation", 0, 1);
  3. anim.setDuration(2000);
  4. anim.start();
  1. class CustomTarget {
  2. private View mView;
  3. private final float x;
  4. private final float y;
  5. public CustomTarget(View view) {
  6. mView = view;
  7. x = view.getX();
  8. y = view.getY();
  9. }
  10. public void setTranslation(float translation) {
  11. mView.setX(translation * x);
  12. mView.setY(translation * y);
  13. }
  14. }

CustomProperty

可以通过自定义Property的形式,实现Target原本没有的属性的动画效果。

代码示例:TextView原先有一个float型的alpha属性,取值为0f ~ 1f,现在给TextView定义一个IntegerAlpha的属性,其取值为0 ~ 256的整型值。

  1. PropertyValuesHolder pvh = PropertyValuesHolder.ofInt(new CustomProperty(Integer.class, "integerAlpha"), 0, 256);
  2. ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTextView, pvh);
  3. anim.setDuration(2000);
  4. anim.start();
  1. class CustomProperty extends Property<TextView, Integer> {
  2. public CustomProperty(Class<Integer> type, String name) {
  3. super(type, name);
  4. }
  5. @Override
  6. public void set(TextView tv, Integer alpha) {
  7. tv.setAlpha((float) alpha / 256);
  8. }
  9. @Override
  10. public Integer get(TextView tv) {
  11. return (int) (tv.getAlpha() * 256);
  12. }
  13. }

ViewPropertyAnimator

ViewPropertyAnimator提供了一种快速便捷的方式,可以直接生成View的动画,多个属性动画同时执行。

  1. float x = mTextView.getX();
  2. float y = mTextView.getY();
  3. mTextView.setX(0);
  4. mTextView.setY(0);
  5. mTextView.setAlpha(0);
  6. ViewPropertyAnimator anim = mTextView.animate();
  7. anim.x(x);
  8. anim.y(y);
  9. anim.alpha(1);
  10. anim.setDuration(3000);
  11. anim.setInterpolator(new BounceInterpolator());
  12. anim.start();

LayoutTransition

LayoutTransition可以设置ViewGroup中布局发生变化时,ChildView的动画

  1. Object o = null;
  2. Animator animIn = ObjectAnimator.ofPropertyValuesHolder(
  3. o,
  4. PropertyValuesHolder.ofFloat("translationX", 200, 0),
  5. PropertyValuesHolder.ofFloat("alpha", 0, 1)
  6. );
  7. Animator animOut = ObjectAnimator.ofPropertyValuesHolder(
  8. o,
  9. PropertyValuesHolder.ofFloat("translationX", 0, 200),
  10. PropertyValuesHolder.ofFloat("alpha", 1, 0)
  11. );
  12. animIn.setDuration(500);
  13. animOut.setDuration(500);
  14. LayoutTransition lt = new LayoutTransition();
  15. mContainer.setLayoutTransition(lt);
  16. lt.setAnimator(LayoutTransition.APPEARING, animIn);
  17. lt.setAnimator(LayoutTransition.DISAPPEARING, animOut);
  18. Button bn = new Button(this);
  19. bn.setText("-");
  20. bn.setOnClickListener(new View.OnClickListener() {
  21. @Override
  22. public void onClick(View v) {
  23. mContainer.removeView(v);
  24. }
  25. });
  26. mContainer.addView(bn, index);
  1. transitionType

    • APPEARING: View出现

    • DISAPPEARING: View消失

    • CHANGE_APPEARING: 由于其他View出现而需要改变位置

    • CHANGE_DISAPPEARING: 由于其他View消失而需要改变位置

    • CHANGING: 由于Layout改变而需要改变位置

  2. 可以给每个TranslationType设置一个Animator,发生变化时,对应的ChildView就会执行相应的动画。

  3. 可以在XML中开启ViewGroup的LayoutTransition属性,布局发生变化时,会有一套默认的动画被执行。

  4. 注意一个比较特别的问题:

    这里执行动画的Target,是布局发生改变的ChildView,因此不需要在Animator中指定Target。但是实际试验发现:

    • 需要用ObjectAnimator实例,并且Target设置为null或者任意对象,动画才能正常执行;
    • 如果用没有Target的ValueAnimator,动画不能正常播放。

一个小坑

ValueAnimator中有一个常用来获取实例的静态方法public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values);

ObjectAnimator继承自ValueAnimator,其中又有一个同名不同参数的静态方法public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values);

调用ObjectAnimator中的ofPropertyValuesHolder且Target传入null时,会默认匹配到ValueAnimator中的同名静态方法。下面的代码本想创建一个Target为null的ObjectAnimator,但实际上创建的是ValueAnimator实例,导致动画不能执行。

  1. Animator animIn = ObjectAnimator.ofPropertyValuesHolder(
  2. null,
  3. PropertyValuesHolder.ofFloat("translationX", 200, 0),
  4. PropertyValuesHolder.ofFloat("alpha", 0, 1)
  5. );

为了让代码正确执行,可以这么写

  1. Object o = null;
  2. Animator animIn = ObjectAnimator.ofPropertyValuesHolder(
  3. o,
  4. PropertyValuesHolder.ofFloat("translationX", 200, 0),
  5. PropertyValuesHolder.ofFloat("alpha", 0, 1)
  6. );
来自为知笔记(Wiz)

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

0

暂无评论

评论前:需填写以下信息,或 登录

用户登录

忘记密码?