1、动画的分类
- View动画(或者叫补间动画)
通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度),从而产生动画效果。
特点:使用简单
缺点:只改变显示,不改变实际属性
安卓中提供了Animation 类库让开发者可以方便实现View动画。
- 帧动画
通过顺序播放一系列的图像从而产生动画效果。可以简单的理解为图片切换动画
缺点:很显然图片过多过大就会导致oom
其实帧动画也是view动画一种,只是他和view动画的表示形式不同。安卓安卓中单独提供了一个类库AnimationDrawable 来实现帧动画。
- 属性动画
通过动态的改变对象的属性从而达到动画效果。
安卓api11才有属性动画,在低版本我们无法直接使用属性动画但是我们可以通过兼容库使用它。
安卓中提供了Animator类库来方便开发者实现属性动画。
2、View动画的使用场景有哪些
view动画除了作用给view之外还有些特殊的场景:
- 在viewGroup中可以控制子元素的出场效果。
- 在activity中可以实现不同activity之间的切换特效
(1)在viewGroup中可以控制子元素的出场效果。
viewGroup都具有个layoutAnimation属性,通过layoutAnimation我们可以吧定义好的动画加载给ViewGroup容器,这样ViewGroup的子类就具有了进出场动画效果。
(2)在activity中可以实现不同activity之间的切换特效
Activity有默认的切换效果,但是我们也可以自定义。主要用到activity的overridePendingTransition这个方法。注意:
- 这个方法在startActivity之后使用才生效
- 或者finish内部super.finish之后使用
- fragment使用时通过FragmentTransaction中的setCustomAnimation()来添加动画,这个切换动画是view动画。
3、View的动画有哪些不足
- 作用的对象只能是view
- 只能播放view的四种动画或动画组合(透明渐变、平移、缩放、旋转)
- 非view对象不能使用view动画
- 太局限 灵活性差
- 补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。(这点可以给按钮设置点击事件进行平移动画测试点击位置)
4、属性动画
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.setDuration(400);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
Log.i("blabla", "onAnimationUpdate: " + value);
}
});
valueAnimator.start();
// log 如下,可以看到数值从0到1平滑的改变。
2019-06-11 10:38:15.289 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.0
2019-06-11 10:38:15.331 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.0
2019-06-11 10:38:15.505 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.36806342
2019-06-11 10:38:15.608 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.7477293
2019-06-11 10:38:15.642 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.8535534
2019-06-11 10:38:15.650 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.89507747
2019-06-11 10:38:15.703 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.9822787
2019-06-11 10:38:15.716 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 0.99554986
2019-06-11 10:38:15.730 25924-25924/com.example.myapplication I/blabla: onAnimationUpdate: 1.0
button = findViewById(R.id.btn);
// 参数1: object 对象
// 参数2: 操作的属性 (属性要有get set方法)
// 3-n : 可变参数 要执行动画的数值变化范围
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(button, "rotation", 0f, 360f);
objectAnimator.setDuration(5000);
objectAnimator.start();
属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
Animator是抽象类,ValueAnimator 是直接继承 Animator的。ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等(Animator的这些方法都在ValueAnimator 实现),确实是一个非常重要的类。所以ValueAnimator 是整个属性动画机制当中最核心的一个类。
5、ObjectAnimator和ValueAnimator 区别
(1)ValueAnimator 的特点
1、对值操作
2、值平滑的发生变化
3、平时使用场景不多
4、ValueAnimator强大到不用针对任何对象而直接进行动画
(2)ObjectAnimator特点
1、对对象的属性进行操作
2、相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类
3、它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的
4、ObjectAnimator是如何进行操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法。对象要有get set方法才能进行属性动画
6、谈谈属性动画的TypeEvaluator(估值器)和ofObject
估值器表示属性从初始值过渡到结束值变化的具体数值,而插值器则表示变化率,比如先加速后减速(默认)、匀速等等。
(1)TypeEvaluator
TypeEvaluator的作用到底是什么呢?简单来说,就是告诉动画系统如何从初始值过度到结束值。也即根据初始值和结束值算出一个具体对应时间的中间值。
举个栗子:
ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
1、FloatEvaluator 实现了TypeEvaluator 接口
2、FloatEvaluator 重写了evaluate方法
3、evaluate方法参数:
- float fraction 表示动画的完成度,我们应该根据它来计算当前动画的值应该是多少。
- Object startValue、Object endValue 动画的初始值和结束值。
4、代码意思:用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。
(2)ofObject
ObjectAnimator objectAnimator =
ObjectAnimator.ofFloat(button, "rotation", 0f, 360f);
objectAnimator.setDuration(5000);
objectAnimator.start();
如上栗子ValueAnimator的ofFloat()方法,用于对浮点型的数据进行动画操作的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。
可以理解为从对象的一种属性状态变换为另一种属性状态
7、属性动画插值器Interpolator
(1) Interpolator 插值器
1、动画播放由快到慢播放、匀速播放、先加速后减速播放。这些效果都是通过插值器来实现的。
2、Interpolator并不是属性动画中新增的技术,实际上从Android 1.0版本开始就一直存在Interpolator接口
3、补间动画也是支持这个功能的
4、属性动画中新增了一个TimeInterpolator接口,这个接口是兼容之前的Interpolator的。
(2) TimeInterpolator常见的系统实现类
- AccelerateDecelerateInterpolator 先加速后减速
- AccelerateInterpolator 加速
- DecelerateInterpolator 减速
- BounceInterpolator 反弹(乒乓球垂直落地在反弹、反弹、效果)
- 等等
(3) 不同Interpolator 实现类如何实现的
由于属性动画提供了TimeInterpolator的兼容接口。我们实现即可。
package android.animation;
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}
1、函数接收一个参数 input
2、参数随着动画的运行而不断变化
3、参数变化有规律,他根据动画的时长从0到1匀速增加。(LinearInterpolator的默认实现 直接返回的input值)
4、 input 是取值范围是 0 到 1,表示当前动画的进度(input只跟时间有关系)百分制进度
(5)input和fraction的关系
input 决定 fraction的值(不同的插值器实现类的getInterpolation()方法实现不同)
fraction =interpolator.getInterpolator()
8、补间动画与逐帧动画没有改变view 位置的原因
View#startAnimation源码
/**
* Start the specified animation now.
*
* @param animation the animation to start now
*/
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
源码可以看出并没有更改与View相关的坐标信息。只是在draw方法中进行了重绘处理。
9、属性动画改变位置的原因
属性动画是在其内部创建一个 AnimationHandler,这个 AnimationHandler 有三个 ArrayList,分别表示活动动画、下一帧动画以及延时动画。它里面通过注册一个 Choreographer 回调去真正的执行动画。