Android动画详解(属性动画、视图动画和帧动画)

Android 动画框架

Android提供了三种动画系统:

  1. Property Animation: 属性动画,3.0引入,比较灵活,能够作用于所有对象
  2. View Animation:视图动画,只能作用于view对象,使用比较简单。
  3. Drawable Animation:帧动画,通过连续的加载 Drawable资源来实现动画效果。

属性动画(Property Animation)

概述

属性动画是一个健壮的框架,能够让你将几乎任何东西做成动画。属性动画能够在给定的时间内,按照给定的策略随时间改变对象的属性值,不论该对象是否能够在屏幕上显示。

使用属性动画只需要做三件事:

  1. 指定动画作用于对象的哪个属性;
  2. 指定动画的时长;
  3. 指定属性的举值区间。

属性动画定义了动画的五个特点: 1. 时长(Duration):即动画持续的时间; 2. 时间插值(Time interpolation):如何根据过去的时间计算属性值,即定义了属性值对时间的函数关系 3. 重复的次数和行为(Repeat count and behavior):重复多少次,能够回放等 4. 动画组(Animator sets):多个动画效果的组合,是同时进行还是顺序执行还是延时执行等 5. 帧刷新频率(Frame refresh delay):默认的帧刷新频率是10ms,你也可以指定,但是这取决于系统的繁忙程度和系统的性能。

属性动画的工作原理

上图是属性动画框架的主要类的关系图。

ValueAnimator记录动画的持续时间、当前的属性值,取值区间等;封装了一个 TimeInterpolator(时间插值器,定义了时间插值因子)、一个 TypeEvaluator(类型估值器,定义了如何根据插值因子计算属性值)。具体工作流程如下:

  1. 动画开始后,ValueAnimator先根据逝去的时间计算 elapsed fraction (逝去时间的比例因子),该值随着时间的过去,在0-1之间取值,0表示刚开始,1表示结束
  2. 计算好elapsed fraction 之后,ValueAnimator调用TimeInterpolator,计算 interpolated fraction(插值因子)
  3. 计算好 interpolated fraction之后,ValueAnimator调用TypeEvaluator,根据插值因子、初始值和结束值,计算当前的属性值。

属性动画和视图动画的区别

视图动画只能作用于view对象,同时只能作用于view对象的一部分属性,只能修改view drawn的位置,不能改变View的实际位置。

相比之下,属性动画更加健壮,它能够动态修改所有对象的任何属性。但是视图动画使用比较简单。所以如果视图动画能够满足需求,可以考虑使用view动画

API 概述

属性动画相关的类大部分定义在 android.animation包下,主要定义了Animator和Evaluator,由于视图动画已经在android.view.animation包下定义了插值器,所以属性动画也使用同样的插值器,当这些插值器不能够满足需求的时候,可以自定义自己的插值器。

使用ValueAnimator

实例化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

ObjectAnimator 是ValueAnimator的子类,它能够直接作用于对象的某个属性,而不必像ValueAnimator一样增加AnimatorUpdateListener监听,对象的属性能够自动更新。

实例化一个ObjectAnimator需要指定对象的属性名称,和取值区间

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

为了ObjectAnimator能够正确工作,必须保证:

  1. 动画作用的属性必须有对应的set方法,如果没有,有三种方法: a. 增加set方法; b.使用包装类; c. 使用ObjectAnimator。
  2. 如果只为取值区间指定一个值,那么这个值会被当做区间的end value,这个时候,会通过属性的get方法获得当前值作为初始值,因此此时需要该属性有对应的get方法;
  3. get和set方法必须作用于相同类型;
  4. 有时需要使用 invalidate() 方法,才能在屏幕上看到动画效果,这取决于视图的属性

使用AnimatorSet

使用AnimatorSet允许将多种动画进行组合,实现动画的同时、顺序、延时播放发等效果。

动画监听

属性动画框架提供了两种监听,用来接收与动画相关的监听,分别是:

  1. Animator.AnimatorListener
  2. ValueAnimator.AnimatorUpdateListener(当使用ValueAnimator时,实现该监听是必须的)

使用TypeEvaluator

估值器的作用是根据 插值因子、初始值和结束值,计算出新的属性值。 属性动画默认支持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的属性其实是没有改变的,使用属性动画真好可以克服这个弊端。为了对view使用属性动画,从3.0开始,Android为view添加了一些新的属性,包括:

  1. translationX 和 translationY:
  2. rotation, rotationX, 和 rotationY
  3. scaleX 和 scaleY
  4. pivotX 和 pivotY:
  5. x 和 y:
  6. alpha

ViewPropertyAnimator提供了一种并行执行多个属性动画的方法,同时代码十分简洁和可读。

在xml中声明属性动画

属性动画框架支持用XML定义属性动画,用来将动画定义和代码分离,实现重用。为了和view动画区分,从3.1开始,用xml定义的属性动画放在 res/animator/ 目录下。

使用xml方式定义属性动画分为以下几步:

  1. 使用xml定义属性动画
  2. 在代码中加载动画,
  3. 为动画设置target
  4. 开启动画。

对UI性能的影响

动画需要额外的渲染工作,与动画相关的工作会被加到 rendering pipeline的动画阶段,使用Profile GPU Rendering工具能够让你定位问题。

视图动画(View Animation)

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 Animation)

帧动画是通过连续的加载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()

你可能感兴趣的:(android,Java)