Android中动画的使用

Android中动画的使用

前言:

在Android中,如果想让一个View的消失,如果没有做任何处理,直接setVisibility(View.GONE),你会发现整个View是瞬间消失的,没有任何过度。为了让用户感知到View的消失,就要让这个View消失的时候带一个动画效果,这样可以减轻用户的焦虑感。

Android中动画的种类

Android中共有三种动画系统,分别是:

  • View Animations - 最原始的Android动画,性能差而且不够灵活.Property Animations动画出现后就被废弃了。
  • Property Animations - 自Android 3.0后引入的强大灵活的动画系统
  • Transition Animations - 上面两种动画只是对某个View进行改变,而不会影响其所在的容器及周边的Views,而Transition动画可以适应layout的变化。默认的Transition框架支持Andorid 4.4以上的系统,使用com.android.support:transition支持库可以使Transition框架兼容到Android 4.0及以上。

Property Animation 属性动画

其中我们最常用的是Property animation,称之为,属性动画。顾名思义,属性动画可以对任意对象的任意属性在一段时长内进行渐变:

Property animations allow us to animate any property of any object from one value to another over a specified duration.

所以,属性动画除了可以对View的位置、角度做变换外,还可以对字体的大小、View的颜色等属性进行变换。

Android中常用的动画叫Property Animation(属性动画),它是出现在View Animation(视图动画) (包括 Tween Animation(补间动画)和 Frame Animation(逐帧动画)) 之后的,相比老的视图动画,Property Animation的主要优势是:

老的View Animation只是对View对象的外观进行动画操作,但是并没有真正的改变对象本身的属性。例如对某个View平移以后,只是视觉特效,并没有真正改变这个View的位置,也就是说你点击平移后的View是不能触发onClick的,因为其属性中的位置还在原来的位置(the previous animations changed the visual appearance of the target objects… but they didn’t actually change the objects themselves.)。而Property Animation除了视觉效果,还能真正的改变View的属性。而使用Property Animation不仅可以对View进行透明度渐变、缩放、平移操作,还可以对其他的一些属性进行动画操作,例如字体大小,颜色,背景等,实现炫酷的字体颜色变化,或者背景颜色变化等等。

Property Animation(属性动画)的主要实现类是:ObjectAnimation。这个类是我们实现动画最常用的类。

ObjectAnimator 动画

用法示例

ObjectAnimator的主要用法如下,假设要让一个View进行透明度从透明到不透明:

1
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(view,"Alpha", 0f, 1f) .setDuration(getResources().getInteger(R.integer.china_date_switch_btn_gone_duration));

上面代码中ofFloat就是指让这个动画按照float类型的精度去变化。ofFloat函数源码中的常用构造函数如下:

1
ofFloat(Object target, String propertyName, float... values)

第一个target是要赋予动画的对象,第二个propertyName是赋予什么动画或者对这个对象的什么属性进行变化,第三个是一个可变长度的参数,那三个点表示这个参数可以有多个,例如上面示例代码中有两个,分别是0f和1f,表示从透明变到不透明。也可以添加任何多个,例如0f, 1f, 0.5f,指的就是从透明到不透明又到半透明。

上面简单的例子就实现了透明度动画。除了透明度属性,常用的属性还有:

常用的propertyName有以下:

| 属性 | 作用 | 数值类型 | | ———— | ——————————— | ——– | | Alpha | 控制View的透明度 | float | | TranslationX | 控制X方向的位移(相对于当前位置) | float | | TranslationY | 控制Y方向的位移(相对于当前位置) | float | | ScaleX | 控制X方向的缩放倍数 | float | | ScaleY | 控制Y方向的缩放倍数 | float | | Rotation | 控制以屏幕方向为轴的旋转度数 | float | | RotationX | 控制以X轴为轴的旋转度数 | float | | RotationY | 控制以Y轴为轴的旋转度数 | float |

除了上面的属性,ObjectAnimator可以对一个对象的任意属性进行动画渐变,例如:

1
2
3
ObjectAnimator.ofFloat(mTextView,"textSize",  0, 100, 50)
.setDuration(5000)
.start();

这样就相当于不断调用TextView的setTextSize()方法对文字大小进行设置,就实现字体变大,再变小的动画。

示例

但是需要注意的是,如果要对一个对象的属性进行变化,这个对象必须有公开的set的方法例如TextView有setTextSize这个方法,那么就可以用“textSize”这个属性。

这时你可能会疑惑了🤔,如果想控制的对象方法名字不是set开头,而是叫changeTextSize或者moveXPosition等,或者一个对象有set方法,但是没有public暴露给大家使用怎么办呢?

这时可以提出两种抛砖引玉的方法:

  • 在对象外层包装一个类,自己写一个public的set方法控制对象的属性。
  • 使用ValueAnimator。至于什么是ValueAnimator,下面就马上会讲到。

ValueAnimator

明白了怎么用,我们再简单了解一下它实现的原理,想一想怎样实现对象的属性动画呢?如果我们想实现一个1秒钟内将透明度从0变到1的动画,其实是把整个工作分成了两部分:

  • 计算属性(透明度)的值。也就是这一秒内,每个时间点的属性值应该是多少,这部分工作由ValueAnimator完成。

  • 设置属性(透明度)的值。将每个时间点的值设置到对象上。这部分由ObjectAnimator完成。

其实ValueAnimator就是一个时间机制,你甚至可以单纯的利用它的时间机制实现除了动画以外的其他逻辑:

1
2
3
4
5
6
7
8
9
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(500);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
// do something with value...
}
});
anim.start();

AnimatorSet

如果需要组合多个动画,可以借助AnimatorSet进行统一管理。AnimatorSet类可以将单个的ObjectAnimator组合起来形成动画集,可以同时创建多个特效,例如,一边放大一边变透明化或者按照顺序先放大再透明化等等。通过组合不同的动画属性,可以满足生活中大部分常用的场景。其用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(view,"Alpha", 0f, 1f)
.setDuration(getResources().getInteger(R.integer.china_date_switch_btn_gone_duration));
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view,"scaleY", 0, 1)
.setDuration(getResources().getInteger(R.integer.china_date_switch_btn_gone_duration));
AnimatorSet fadeInAnim = new AnimatorSet();
fadeInAnim.play(alphaAnim).with(scaleAnim);
fadeInAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
view.setVisibility(View.VISIBLE);
}
});
fadeInAnim.start();

两大神器TypeEvaluator和Interpolation

TypeEvaluator

恐怕不少刚接触ObjectAnimator的人都会想为啥ObjectAnimator后面要跟ofFloat, ofInt和ofObject,ofObject又是个什么东西呢?

如果一个动画变化的属性是数字,例如透明度就是从0到1的,ofFloat会在要求的时间范围内把每个时间点的值计算成一个个的float值,相应的,ofInt就会把属性值计算成Int值,这些都是动画系统实现的方法。那如果现在属性的变化不是时间而是Point类,那动画系统怎么知道如何计算每个时间点的Point值呢,答案是你来告诉动画系统如何计算。只要继承TypeEvaluator类,实现其中的evaluate方法即可:

1
2
3
4
5
6
7
8
public class PointEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
return new Point(startPoint.x + fraction * (endPoint.x - startPoint.x),
startPoint.y + fraction * (endPoint.y - startPoint.y));
}
}

这样就可以计算两个点之间的值了:

1
2
3
Point p0 = new Point(0, 0);
Point p1 = new Point(100, 200);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), p0, p1);
Interpolation

放大,缩小,平移等这样的动画方式非常普通,因为这些动画是线性的,也就是说在动画时间内,属性值的变化是线性变化的。大家有时会看到一些非常炫酷的动画例如,回弹,果冻效果等等,这些效果多是非线性变化的结果,如果想实现这样的效果,就用到了Interpolation(插值器)。

Android已经实现了常用的Interpolation,如:

| Name | Description | | ———————- | ———– | | AccelerateInterpolator | 加速 | | BounceInterpolator | 弹跳 | | DecelerateInterpolator | 减速 |

还有AccelerateDecelerateInterpolator、AnticipateInterpolator、PathInterpolator、OvershootInterpolator、AnticipateOvershootInterpolator 、DecelerateInterpolator 、CycleInterpolator等,这些基本上涵盖了我们使用的范围。

当然,如果上面插值器不满足需求,也可以定制插值器,只需要继承BaseInterpolator并实现TimeInterpolator接口就可以了,例如OvershootInterpolator的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class OvershootInterpolator extends BaseInterpolator {
private final float mTension;

public OvershootInterpolator() {
mTension = 2.0f;
}

public OvershootInterpolator(float tension) {
mTension = tension;
}

public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
}

这里的getInterpolation方法里是一个插值器函数,如果输入的t直接返回,那这就是一个线性插值器。这个网站看一查看插值器函数生成的曲线,安利给大家。

Android Transition Framework

上面介绍的动画针对的是某一个对象进行动画操作,但是实际应用中,我更多的使用Android的Transition动画来移动某个View。Transition动画与Property动画最大的区别就是:View动的过程中会对其周边的View产生影响。也就是说,如果我想让一个View逐渐变大,那它在变大的过程中不会覆盖在其周边的View上面,而是挤推着周围的View移动,也就是整个Layout都在变化,而这一切都是Transition框架自动实现的,开发者不需要关心实现细节。

后续补充:

https://developer.android.com/training/transitions/

https://developer.android.com/training/transitions/start-activity

https://cloud.tencent.com/info/8fb508a6e6115e59e4f151fbaadd432e.html

参考:

ObjectAnimator 基本使用 http://wiki.jikexueyuan.com/project/android-animation/7.html

Android 属性动画:这是一篇很详细的 属性动画 总结&攻略 https://blog.csdn.net/carson_ho/article/details/72909894

为什么要废弃掉Android之前的动画?https://android-developers.googleblog.com/2011/02/animation-in-honeycomb.html

查看Interpolation曲线:http://inloop.github.io/interpolator/

Author

calvinche

Posted on

2018-12-02

Licensed under

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×