Android动画

Android的动画可以分为三种:View动画、帧动画和属性动画,其实帧动画也属于View动画的一种,只不过它和平移、旋转等常见的View动画在表现形式上不同而已。

View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画。

帧动画通过数序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画,图片过多过大就会导致OOM(Out Of Memory,指的是kernel因分配不出内存而报的错误)。

属性动画通过动态的改变对象的属性从而达到动画效果。

 

一、View动画

View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation.这四种动画既可以通过XML定义,也可以通过代码来动态创建,建议采用XML来定义动画。

名称 标签 子类 效果
平移动画 <translate> TranslateAnimation 移动View
缩放动画 <scale> ScaleAnimation 放大缩小View
旋转动画 <rotate> RotateAnimation 旋转View
透明度动画 <alpha> AlphaAnimation 改变View的透明度
<!--
  android:interpolator
  设置动画集合所采用的插值器,默认值为@android:anim/accelerate_decelerate_interpolator

  android:shareInterpolator
  Boolean. 表示集合中的动画是否共享集合的插值器。当值为true且集合没有设置插值器,
  此时集合中的动画就会使用默认的插值器@android:anim/accelerate_decelerate_interpolator,
  但是你也可以为集合中的动画单独指定所需的插值器。
-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator=["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
    <set>
        ...
    </set>
</set>

<set>标签表示补间动画的集合,对应于AnimationSet类,所以上面语法中的<set>标签可以包含多个补间动画的标签;并且补间动画的集合中还可以包含补间动画的集合。

 

1、translate 位置转移动画

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="2000"
        android:fromXDelta="30"
        android:fromYDelta="30"
        android:toXDelta="400"
        android:toYDelta="300" />
</set>

代码方式:

Animation loadAnimation = new TranslateAnimation(30.0f, 400.0f, 30.0f, 300.0f);
loadAnimation.setDuration(2000);
imageView.startAnimation(loadAnimation);

 

2、scale 尺寸伸缩动画

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="2000"
        android:fillAfter="false"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.4"
        android:toYScale="1.4" />
</set>

代码方式:

Animation loadAnimation = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
loadAnimation.setDuration(2000);
imageView.startAnimation(loadAnimation);

 

3、rotate 旋转动画

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="2000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="+350" />
</set>

代码方式:

Animation loadAnimation = new RotateAnimation(0.0f, +350.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
loadAnimation.setDuration(2000);
imageView.startAnimation(loadAnimation);

 

4、alpha 透明度动画

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="2000"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

代码方式:

Animation loadAnimation = new AlphaAnimation(0.0f, 1.0f);
loadAnimation.setDuration(2000);
imageView.startAnimation(loadAnimation);

 

5、自定义动画

自定义动画需要集成Animation抽象类,然后重写它的initialize和applyTransformation方法,

在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程(需要数学知识)。

 

二、帧动画

帧动画是顺序播放一组预定义好的图片,类似于电影播放。系统提供了AnimationDrawable类来使用帧动画。

frame帧动画

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true" >
    <item
        android:drawable="@drawable/pic01"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/pic02"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/pic03"
        android:duration="1000"/>
</animation-list>

xml帧动画使用代码:

imageView.setImageResource(R.drawable.pic_frame);
AnimationDrawable animation = (AnimationDrawable) imageView.getDrawable();
animation.setOneShot(false);//循环
animation.start();

帧动画的使用比较简单,但是比较容易引起OOM,所以在使用的时候尽量避免使用过多尺寸较大的图片。

 

三、属性动画

属性动画是API11(Android3.0)加入的新特性,有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通过它们可以实现绚丽的动画。

其中ObjectAnimator继承自ValueAnimator,AnimatorSet是属性动画集,可以定义一组动画。

只要某个类具有属性(即该类含有某个字段的set和get方法),那么属性动画框架就可以对该类的对象进行动画操作。

属性动画框架工作原理:
1 在创建属性动画时如果没有设置属性的初始值,此时Android系统就会通过该属性的get方法获取初始值,所以在没有设置属性的初始值时,必须提供该属性的get方法,否者程序会Crash。
2 在动画播放的过程中,属性动画框架会利用时间流逝的百分比获取属性值改变的百分比(时间插值器TimeInterpolator),接着利用获取的属性值改变的百分比获取改变后的属性值(类型估值器TypeEvaluator)。
3 通过该属性的set方法将改变后的属性值设置到对象中。

<!--
  android:ordering
  该属性有如下两个可选值:
  together:表示动画集合中的子动画同时播放。
  sequentially:表示动画集合中的子动画按照书写的先后顺序依次播放。
  该属性的默认值是together。
-->
<set
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <set>
        ...
    </set>
</set>

<set>对应AnimatorSet类,<objectAnimator>对应ObjectAnimator类,<animator>标签对应ValueAnimator类。

在实际开发中建议使用代码方式来实现属性动画,因为很多时候属性是无法提前确定的。

1、使用属性动画代码实现上边View动画的效果:

 //平移动画
ObjectAnimator translate1 = ObjectAnimator.ofFloat(imageView, "translationX", 20, 100);
translate1.setDuration(2000);
ObjectAnimator translate2 = ObjectAnimator.ofFloat(imageView, "translationY", 20, 100);
translate2.setDuration(2000);
translate1.start();
translate2.start();
//伸缩动画
ObjectAnimator scale1 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.5f);
scale1.setDuration(2000);
scale1.setRepeatCount(ValueAnimator.INFINITE);
scale1.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator scale2 = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 0.5f);
scale2.setDuration(2000);
scale2.setRepeatCount(ValueAnimator.INFINITE);
scale2.setRepeatMode(ValueAnimator.REVERSE);
scale1.start();
scale2.start();
//透明度动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0, 1)
    .setDuration(2000);
alpha.setRepeatCount(ValueAnimator.INFINITE);
alpha.setRepeatMode(ValueAnimator.RESTART);
alpha.start();
//旋转动画
ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0, 360)
    .setDuration(2000);
rotate.setRepeatCount(ValueAnimator.INFINITE);
rotate.setRepeatMode(ValueAnimator.RESTART);
rotate.start();

 

2、对任意属性做动画

举例改变Width代码,这里有两种方式,效果是一样的:

方式一,包装原始对象,使用ObjectAnimator实现动画:

//包装原始对象,为其提供set、get方法
public static class ViewWrapper {
    private View mTarget;

    public ViewWrapper(View mTarget) {
        this.mTarget = mTarget;
    }

    public int getWidth() {
        return mTarget.getLayoutParams().width;
    }

    public void setWidth(int width) {
        mTarget.getLayoutParams().width = width;
        mTarget.requestLayout();
    }
}
//启动动画
ViewWrapper wrapper = new ViewWrapper(imageView);
ObjectAnimator animation = ObjectAnimator.ofInt(wrapper, "width", 10, 600).setDuration(3000);
animation.setRepeatMode(ValueAnimator.REVERSE);
animation.setRepeatCount(ValueAnimator.INFINITE);
animation.setInterpolator(new DecelerateInterpolator());
animation.start();

方式二,使用ValueAnimator监听动画过程,实现属性变化

ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

    private IntEvaluator intEvaluator = new IntEvaluator();

    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
    float fraction = valueAnimator.getAnimatedFraction();
    imageView.getLayoutParams().width = intEvaluator.evaluate(fraction, 10, 600);
    imageView.requestLayout();
    }
});
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setDuration(3000).start();

 

3、属性动画集

 //平移动画
ObjectAnimator translate1 = ObjectAnimator.ofFloat(imageView, "translationX", 20, 100);
translate1.setDuration(2000);
ObjectAnimator translate2 = ObjectAnimator.ofFloat(imageView, "translationY", 20, 100);
translate2.setDuration(2000);
//伸缩动画
ObjectAnimator scale1 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.5f);
scale1.setDuration(2000);
ObjectAnimator scale2 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.5f);
scale2.setDuration(2000);
//透明度动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0, 1)
    .setDuration(2000);
//旋转动画
ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0, 360)
    .setDuration(2000);
rotate.setRepeatCount(ValueAnimator.INFINITE);
rotate.setRepeatMode(ValueAnimator.REVERSE);
//    //平移动画集
//    AnimatorSet translateSet = new AnimatorSet();
//    translateSet.playTogether(translate1, translate2);
//    translateSet.start();
//    //伸缩动画集
//    AnimatorSet scaleSet = new AnimatorSet();
//    scaleSet.playTogether(scale1, scale2);
//    scaleSet.start();
//串行执行动画集
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(translate1, alpha, rotate);
animatorSet.start();

 

四、使用动画的注意事项

1、OOM问题

这个问题主要出现在帧动画中,当图片数量较多切图片较大时就容易出现OOM,这个实际开发中要注意,尽量表面使用帧动画。

2、内存泄漏

在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时即使停止,否则将导致Activity无法释放从而造成内存泄漏,View动画不存在此问题。

3、兼容性问题

动画在3.0以下的系统上有兼容性问题,在某些特殊场景可能无法正常工作,因此要做好适配。

4、View动画问题

View动画是对View的影像做动画,并不是真正的改变View的状态,因此有时候会出现动画完成后View无法隐藏的现象,即setVisibility(View.GONE)失效了,这个时候只要调用View.clearAnimation()清除View动画即可。

5、不要使用px

在进行动画的过程中,要尽量使用dp,使用px会导致在不同的设备上有不同的效果。

6、动画元素交互

将View移动后,在Android3.0以前的系统上,不管是View动画还是属性动画,新位置无法触发单击事件。同时,老位置仍然可以出发单击事件。尽管View已经在视觉上不存在了,将View移回原位置后,原位置的单击事件继续生效。从3.0开始,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然是在原位置。

7、硬件加速

使用动画的过程中,建议开启硬件加速(在manifest中Application、Activity级别上声明android:hardwareAccelerated="true"即可),这样会提高动画的流畅性。

 

五、本文代码如下

Android动画示例

 

posted @ 2017-03-20 11:51  竹下半碗茶  阅读(649)  评论(0编辑  收藏  举报