Android的动画理解与深入

在android开发中,动画能让我们做出各种各样酷炫的效果,然而,你真的懂动画吗?为什么有三种动画的分类?他们的实现原理是怎么样的?我们怎么样才能做出绚丽的动画效果?动画使用中有什么值得注意的地方?本文,就将进行以上问题的一些探讨。


一、Android的动画的分类

我们都知道Android的动画分成三种,补间动画(View Animation)、帧动画(Frame)、属性动画(Property Animation,Android 3.0之后增加的)。

帧动画,所谓帧动画其实很简单,它就是按顺序播放设定好的一组图片,就像电影那样,每一帧都是一个画面,就可以形成动画的效果。然而这种动画有很大的局限性,首先它需要你提供很多图片,然后加载动画的时候,如果图片过多会很容易出现OOM。所以现在已经很少使用这种方式去定义动画了

补间动画(View Animation),也叫View动画,它的作用对象是View,不需要预设很多图片。

属性动画(Property Animation),这种动画是android 3.0之后引入的,具有很高的扩展性。也是我们常用的动画。

二、帧动画

帧动画其实就是顺序播放一组预先定义好的图片,类似于电影播放,很容易因为加载太多的图片,导致OOM。虽然现在这种动画使用并不多。

但是我们也需要掌握如何去使用它。

它的使用方法很简单:
首先,通过XML定义一个AnimationDrawable,里面就将一张张图片按顺序排列
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/iamge1" android:duration="500"/>
    <item android:drawable="@drawable/iamge2" android:duration="500"/>
    <item android:drawable="@drawable/iamge3" android:duration="500"/>
</animation-list>

然后,将这个AnimationDrawble作为View的背景
最后,通过View对象,获取到这个Drawable,通过AnimationDrawble的start方法,来开启动画
TextView view = (TextView) findViewById(R.id.frame_anim);
view.setBackground(R.anim.frame_anim);
AnimationDrawable drawable = (AnimationDrawable) view.getBackground();
drawable.start();

这样我们就实现了一个帧动画。


三、补间动画

View动画支持四种动画效果,旋转(rotate)、平移(translate)、缩放(scale)、透明度(alpha),这四种变换效果分别对应了Animation的四个子类,RotateAnimation、TranslateAnimation、ScaleAnimation、AlphaAnimation,这四种动画即可以通过XML定义,也可以通过代码来动态创建。View动画即可以是单个动画,也可以是一系列动画组成的。
可以通过AniamtionSet来创建动画集合 。
View动画是作用于View的影像上的,所以会存在一些问题,这些问题后面我们会讲。
TranslateAnimation  平移动画,可以让控件在水平和竖直方向完成平移效果,可以指定x和y轴的开始点坐标和结束点坐标
ScaleAnimation    缩放动画,使View具有放大或者缩小的动画效果,可以指定水平和竖直方向的缩放起始值和结束值,以及缩放的轴点的x和y坐标,轴点会影响缩放的效果,默认的轴点是View的中心点,如果把轴点设置成View的右边界,那么View只会向右边缩放
RotateAnimation     旋转动画,它可以使View具有旋转的动画效果,可以设置旋转的开始角度和结束的角度,以及旋转的轴点的x和y坐标,在旋转动画中轴点扮演旋转轴的角色,即View是围绕轴点进行旋转的
AlphaAnimation      透明度动画,他可以改变View的透明度,可以设置透明度的起始值和结束值

除了每个动画可以设置的值之外,还可以设置一些公共的值,比如动画时间、动画的插值器、是否停留在动画结束位置等

View动画的使用有两种方式
1、定义在xml文件里面,然后通过AnimationUtils去加载出Animation对象,然后用View.startAnimaion(animation对象),去执行动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <translate 
        android:toXDelta="100"
        android:toYDelta="100"
        android:fromXDelta="0"
        android:fromYDelta="0"/>
    <rotate
        android:fromDegrees="0"
        android:toDegrees="90"/>
</set>
上述代码,就定义了一个动画的集合,包含两种动画平移和旋转。
TextView view = (TextView) findViewById(R.id.text_view );
Animation animation = AnimationUtils.loadAnimation(this, R.anim.view_anim);
view.startAnimation(animation);

2、除了用xml之外,我们可以通过在代码里面编写动画
TextView view = (TextView) findViewById(R.id.text_view);
AlphaAnimation animation = new AlphaAnimation(0,1);//透明度从0到1,1指的是完全不透明
animation.setDuration(500);//设置动画时间。500ms
view.startAnimation(animation);

我们灵活的使用,android提供的四种动画方式,就可以实现大部分的动画效果了。但是如果有这四种做不了的动画效果的话,我们应该怎么做呢?
那就需要进行自定义动画了

3、自定义View动画
比如,我们需要实现控件沿竖直方向的中心轴旋转动画,那么通过提供的动画就无法很容易的实现效果。这时候我们需要进行自定义View动画。
通过观察,它原生的四种动画,我们可以发现

它都是通过,实现Aniamtion类,然后去实现具体的效果。那我们也可以自定义类,去实现Aniamtion类
public class CustomAnimation extends Animation{
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);//一些初始化工作
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);//进行矩阵变换
    }
}
这样就可以实现一个自己的动画类,但是即简单又复杂,简单是因为自定义动画,继承Animation,重写它的两个方法即可,复杂是因为通过
矩阵变换,实现动画效果,这涉及到Matrix和Camera等的使用,需要一些数学知识,这里就不做深入讨论了。
网上有一个实现好的y轴旋转的动画,它的applyTransformation的具体实现如下
 @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        //该方法,随着动画的执行会一直被回调
        //interolatedTime  该值其实就是从0 到 1之间的一个变化值,该值的变化方式跟动画插值器有关系
        //Transformation  其实就是保存当前帧的矩阵和alpha值的一个对象,在view进行绘制的时候,会从动画对象中拿出该对象,
       //根据它保存的matrix和alpha值,进行view的绘制
        final float fromDegrees = mFromDegrees;  
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);  
  
        final float centerX = mCenterX;  //该动画的实现,是通过Camera进行配合实现的
        final float centerY = mCenterY;  
        final Camera camera = mCamera;
  
        final Matrix matrix = t.getMatrix();
  
        camera.save();  
        if (mReverse) {  
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);  
        } else {  
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));  
        }  
        camera.rotateY(degrees);  
        camera.getMatrix(matrix);  
        camera.restore();  
  
        matrix.preTranslate(-centerX, -centerY);  //进行矩阵变换,通过上述的代码就确认了当前帧的矩阵
        matrix.postTranslate(centerX, centerY);  
    }  

这里就不对Matrix和Camera进行深入讨论了。
通过以上的方式,我们就实现了一个y轴旋转的自定义动画

4、View动画的实现原理
在上面,我们讨论了View动画的使用,已经自定义View动画,那么View动画的实现原理到底是怎么一回事呢?
View动画的四种动画都继承了Animation类,重写了applyTransformation方法,这个方法主要是把一些属性组装成一个Transformation类,这个方法会被父类的getTransformation方法调用,Transformation类中封装了矩阵和alpha值,Canvas类包括了当前矩阵,当绘制的时候,会进行一次矩阵运算,然后把运算结果显示在Canvas上,这样通过不断修改Canvas的矩阵并刷新屏幕,就可以让View的对象不停的做图形变换了。 
其实一个View的动画实现过程是这样的:
首先,View的startAnimation方法,开启这个动画

该方法其实就是View中的代码,源码如上,其实就是set了这个anim对象,然后调用了view的invalidate方法,开启view的绘制
setAnimation方法的源码如下

这个方法做的事情还是很简单,就是把这个动画,保存为view的mCurrentAniamtion
然后,invalidate方法触发了onDraw函数
在View的draw函数中:
会先得到View的当前动画的对象
getAnimation方法,其实就是返回了上面我们保存的mCurrentAniamtion对象
因为View的这块源码太多,我们就不进行深入理解了,有兴趣的可以自行查看
后面的大致逻辑如下
然后进行一系列的逻辑判断,调用动画对象的getTransformation方法,得到当前时间点的矩阵
将该矩阵设置成Canvas的当前矩阵
调用canva的draw方法,绘制屏幕
判断getTransformation的返回值,若为真,调用invalidate方法,刷新屏幕进入下一帧,若为假,说明动画完成

这样,随着动画的执行,会一直调用view的draw方法,去绘制当前的图形,然后动画负责图片的矩阵变换和alpha值变换,就完成了一个动画


四、属性动画

属性动画是android 3.0之后加入的新动画,它对作用对象进行了扩展,它可以对任意对象的属性进行动画而不仅仅是View,
它能达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变,因此,属性动画几乎是无所不能的,
只要对象有这个属性,他都能实现动画效果。
属性动画常用的几个类有ValueAnimator、ObjectAnimator和AnimatorSet
其中ObjectAnimator也是继承自ValueAnimator
AnimatorSet是动画集合,可以定义一组动画

属性动画的几个比较关键的参数有:
propertyName :    属性动画的作用对象的属性的名称
duration   :       动画时长
valueFrom:     属性的起始值
valueTo    :     属性的结束值
repeatCount:  动画的重复次数
repeatMode :  动画的重复模式

1、属性动画的使用
A、在代码中进行编写
TextView view = (TextView) findViewById(R.id.frame_anim);
ObjectAnimator animator = ObjectAnimator.ofFloat(view,//动画的作用对象
        "translationY",//作用对象的属性,这里是让他在y轴平移 
         0,//起始属性值
         view.getWidth());//属性值的结束值
animator.setDuration(300);
animator.start();
上述代码 ,就给TextView设置了一个沿y轴平移的动画

B、 也可以在xml中定义,然后通过属性动画加载器,去加载这个动画
 TextView view = (TextView) findViewById(R.id.frame_anim);
 AnimatorSet animSet = AnimatorInflater.loadAnimator(this, R.anim.demo_anim);
 animSet.setTarget(view);
 animSet.start();
然后将给动画设置作用对象,然后开启动画

我们也可以通过给动画设置插值器的方式,去控制动画的执行效果,比如是匀速执行的,还是先加速再减速等,插值器其实是通过一些数学公式去实现的

2、给View的任意属性做动画
虽然我们说属性动画可以给任意的属性设置动画,但是还是有一些点需要注意的地方,想要对属性abc设置动画,需要满足两个条件
A、对象必须提供setAbc的方法,如果动画的时候,没有传递初始值,那么还需要提供getAbc方法
B、对象的setAbc方法,对属性abc做出的改变必须能够通过某种方式反映出来,比如UI的改变之类的(如果不满足这一条,那么动画没有效果)

3、ValueAnimator的介绍及使用
前面我们使用的ObjectAnimator是继承自ValueAnimator的,ValueAnimator本身不作用于任何对象,也就是说直接使用它没有任何的效果,
它可以对一个值做动画,然后我们可以监听其值的改变过程,在动画过程中去修改我们的对象的属性值,这样就相当于我们对对象做了动画
  final TextView view = (TextView) findViewById(R.id.frame_anim);
  ValueAnimator animator = ValueAnimator.ofInt(1,100);//设置属性从0到100进行改变
  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//设置改变过程的监听
       @Override
       public void onAnimationUpdate(ValueAnimator animation) {
           int animatedValue = (int) animation.getAnimatedValue();//得到实时的改变值
           view.getLayoutParams().width = animatedValue;//给作用对象的指定属性设置值
           view.requestLayout();//当layout有改变时,调用此方法通知更新View
       }
   });
   animator.setDuration(500);
   animator.start();

上述代码就将view的宽度,在500ms的时间里,从1变成了100

4、属性动画的工作原理
属性动画其实就是根据你传递的该属性的初始值和最终值,以多次动画效果通过反射的方式去调用作用对象的set方法,
去改变作用对象的属性,从而实现动画的效果。如果没有传初始值的话,还需要提供get方法


五、Android动画的使用注意事项

A、OOM问题
主要出现在帧动画里面,如果图片较多且图片较大时,就很容易出现OOM
B、内存泄露问题
在属性动画里,有一类无限循环的动画,这类动画需要在Activity退出的时候及时停止,否则将导致Activity无法释放,从而造成内存泄露
C、View动画的问题
View动画,是对View的影像做动画,并不会真正的改变View的状态
D、动画元素的交互
将View移动后,在Android 3.0之前,无论是View动画还是属性动画,新的位置均无法触发单击事件,同时老位置任然可以触发单击事件,从3.0后,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然在原位置


到此,个人的动画的一些理解和总结就差不多了,但是还有很多值得深入的地方,比如矩阵变换和Camera方法的知识,然后属性动画的
实现,是通过反射的方式,拿到对象的方法,从而去设置对象的值等等。
学无止境,因个人水平有限,难免有错误之处,请个人指正批评。很多知识也来源于其他人无私的奉献,感谢所有有共享和开源精神的同业者们!




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值