Android提供了三种动画系统:
属性动画是一个健壮的框架,能够让你将几乎任何东西做成动画。属性动画能够在给定的时间内,按照给定的策略随时间改变对象的属性值,不论该对象是否能够在屏幕上显示。
使用属性动画只需要做三件事:
属性动画定义了动画的五个特点: 1. 时长(Duration):即动画持续的时间; 2. 时间插值(Time interpolation):如何根据过去的时间计算属性值,即定义了属性值对时间的函数关系 3. 重复的次数和行为(Repeat count and behavior):重复多少次,能够回放等 4. 动画组(Animator sets):多个动画效果的组合,是同时进行还是顺序执行还是延时执行等 5. 帧刷新频率(Frame refresh delay):默认的帧刷新频率是10ms,你也可以指定,但是这取决于系统的繁忙程度和系统的性能。
上图是属性动画框架的主要类的关系图。
ValueAnimator记录动画的持续时间、当前的属性值,取值区间等;封装了一个 TimeInterpolator(时间插值器,定义了时间插值因子)、一个 TypeEvaluator(类型估值器,定义了如何根据插值因子计算属性值)。具体工作流程如下:
视图动画只能作用于view对象,同时只能作用于view对象的一部分属性,只能修改view drawn的位置,不能改变View的实际位置。
相比之下,属性动画更加健壮,它能够动态修改所有对象的任何属性。但是视图动画使用比较简单。所以如果视图动画能够满足需求,可以考虑使用view动画
属性动画相关的类大部分定义在 android.animation包下,主要定义了Animator和Evaluator,由于视图动画已经在android.view.animation包下定义了插值器,所以属性动画也使用同样的插值器,当这些插值器不能够满足需求的时候,可以自定义自己的插值器。
实例化ValueAnimator通常通过工厂方法: ofInt(), ofFloat()或者 ofObject()等,例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
如果要使用动画产生的插值,可以通过设置 AnimatorUpdateListener,例如:
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
// You can use the animated value in a property that uses the
// same type as the animation. In this case, you can use the
// float value in the translationX property.
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
拿到产生的插值根据需要使用。
ObjectAnimator 是ValueAnimator的子类,它能够直接作用于对象的某个属性,而不必像ValueAnimator一样增加AnimatorUpdateListener监听,对象的属性能够自动更新。
实例化一个ObjectAnimator需要指定对象的属性名称,和取值区间
ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();
为了ObjectAnimator能够正确工作,必须保证:
使用AnimatorSet允许将多种动画进行组合,实现动画的同时、顺序、延时播放发等效果。
属性动画框架提供了两种监听,用来接收与动画相关的监听,分别是:
估值器的作用是根据 插值因子、初始值和结束值,计算出新的属性值。 属性动画默认支持int, float或 color类型的属性,分别对应 IntEvaluator, FloatEvaluator和 ArgbEvaluator,当动画的作用的属性值类型不是Android支持的类型时,需要自己实现估值器,只需要继承TypeEvaluator ,并实现evaluate(float fraction, Object startValue, Object endValue) 方法。
插值器的作用是,根据elapsed fraction,计算插值因子,它定义了插值因子和elapsed fraction之间的函数关系。不同的插值器定义了不同的插值因子的计算方法,从而产生不同的动画效果。Android预定义了多种插值器,能够满足日常的开发需求,如果开发需求无法满足,可以自定义实现插值器。实现 TimeInterpolator接口即可。
一个关键帧是一个时间/值对,用来指定动画在某一时刻的状态,每个关键帧同样可以拥有插值器,用来控制上一帧与该帧之间的行为。其用法如下:
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);
使用 ofInt(), ofFloat()或 ofObject()等方法实例化关键帧,然后,使用关键帧定义一个PropertyValuesHolder,最后实例化一个ObjectAnimator就可以了。
view动画只能改变view在屏幕上绘制的位置,并不能改变view的实际位置,因为view的属性其实是没有改变的,使用属性动画真好可以克服这个弊端。为了对view使用属性动画,从3.0开始,Android为view添加了一些新的属性,包括:
ViewPropertyAnimator提供了一种并行执行多个属性动画的方法,同时代码十分简洁和可读。
属性动画框架支持用XML定义属性动画,用来将动画定义和代码分离,实现重用。为了和view动画区分,从3.1开始,用xml定义的属性动画放在 res/animator/ 目录下。
使用xml方式定义属性动画分为以下几步:
动画需要额外的渲染工作,与动画相关的工作会被加到 rendering pipeline的动画阶段,使用Profile GPU Rendering工具能够让你定位问题。
View Animation系统是用来对view执行补间动画的,它主要是对view的内容进行大小、位置、旋转和透明度的一系列变换实现的。
使用视图动画有两种方式,代码实现或者使用xml定义,推荐使用xml方式,这种方式将动画的定义和代码分离,避免硬编码,提供复用性。
示例:
首先定义一个xml,放置在res/anim/目录里,
然后在代码中这样使用:
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);
先加载动画,然后播放动画。
也可以给动画设置一个播放的时间,然后在设置给view。
注意:不管动画怎么变,view的边界都不会变动,即使动画超过了view的边界,也不会变形或者裁剪,除非超过了父View的边界。
帧动画是通过连续的加载drawable资源的方式实现动画效果的,就好像放电影。AnimationDrawable 是帧动画的基础。
帧动画有两种定义方式,一种是在代码中使用AnimationDrawable,另一种是使用xml定义,后者比较简单。
下面是帧动画的常用使用方式:
`
`
首先定义一个xml文件,保存在res/drawable/ 目录下,然后在代码里:
AnimationDrawable rocketAnimation;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
rocketAnimation.start();
return true;
}
return super.onTouchEvent(event);
}
先将帧动画设置为imageview的背景,然后获得AnimationDrawable,最后开启动画。
注意:不要在activity的oncreate()回调里调用start方法,因为此时动画还没有完全attached 到window上,如果需要在不需要用户交互的时候立马执行动画,可以考虑在 activity的onWindowFocusChanged()里调用start()