动画分类

Android动画可以分3种:View动画,帧动画和属性动画;属性动画为API11的新特性,在低版本是无法直接使用属性动画的,但可以用nineoldAndroids来实现(但是本质还是viiew动画)。学习本篇内容主要掌握以下知识:

1,View动画以及自定义View动画。
2,View动画的一些特殊使用场景。
3,对属性动画做了一个全面的介绍。
4,使用动画的一些注意事项。


view动画

  • View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation(平移动画)、ScaleAnimation(缩放动画)、RotateAnimation(旋转动画)和AlphaAnimation(透明度动画),他们即可以用代码来动态创建也可以用XML来定义,推荐使用可读性更好的XML来定义。
  • <set>标签表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且他的内部也可以嵌套其他动画集合。android:interpolator 表示动画集合所采用的插值器,插值器影响动画速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。
    android:shareInterpolator表示集合中的动画是否和集合共享同一个插值器,如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或默认值。
  • Animation通过setAnimationListener方法可以给View动画添加过程监听。
  • 自定义View动画只需要继承Animation这个抽象类,并重写initialize和applyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩形变换,很多时候需要采用Camera来简化矩形变换过程。
  • 帧动画是顺序播放一组预先定义好的图片,类似电影播放;使用简单但容易引发OOM,尽量避免使用过多尺寸较大的图片。

  • view动画应用场景

    LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,当他的子元素出场的时候都会具有这种动画,ListView上用的多,LayoutAnimation也是一个View动画。
    代码实现:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:animationOrder="normal"
    4. android:delay="0.3" android:animation="@anim/anim_item"/>
    5.  
    6. //--- animationOrder 表示子元素的动画的顺序,有三种选项:
    7. //normal(顺序显示)、reverse(逆序显示)和random(随机显示)。
    8.  
    9. <?xml version="1.0" encoding="utf-8"?>
    10. <set xmlns:android="http://schemas.android.com/apk/res/android"
    11. android:duration="300"
    12. android:shareInterpolator="true">
    13. <alpha
    14. android:fromAlpha="0.0"
    15. android:toAlpha="1.0" />
    16. <translate
    17. android:fromXDelta="300"
    18. android:toXDelta="0" />
    19. </set>

    第一种,在布局中引用LayoutAnimation

    1. <ListView
    2. android:id="@+id/lv"
    3. android:layout_width="match_parent"
    4. android:layout_height="0dp"
    5. android:layout_weight="1"
    6. android:layoutAnimation="@anim/anim_layout"/>

    第二种,代码种使用

    1. Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
    2. LayoutAnimationController controller = new LayoutAnimationController(animation);
    3. controller.setDelay(0.5f);
    4. controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
    5. listview.setLayoutAnimation(controller);

    帧动画

      逐帧动画(Frame-by-frame Animations)从字面上理解就是一帧挨着一帧的播放图片,类似于播放电影的效果。不同于View动画,Android系统提供了一个类AnimationDrawable来实现帧动画,帧动画比较简单,我们看一个例子就行了。
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:oneshot="false">
    4.  
    5. <item
    6. android:drawable="@mipmap/lottery_1"
    7. android:duration="200" />
    8. // ...省略很多
    9. <item
    10. android:drawable="@mipmap/lottery_6"
    11. android:duration="200" />
    12.  
    13. </animation-list>

    然后

    1. imageView.setImageResource(R.drawable.frame_anim);
    2. AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
    3. animationDrawable.start();//启动start,关闭stop

    属性动画

    属性动画是Android 3.0新加入(api 11)的功能,不同于之前的view动画(看过的都知道,view动画比如实现的位移其实不是真正的位置移动,只是实现了一些简单的视觉效果)。属性动画对之前的动画做了很大的拓展,毫不夸张的说,属性动画可以实现任何动画效果,因为在作用的对象是属性(对象),属性动画中有几个概念需要我们注意下,

    ValueAnimator、ObjectAnimator、AnimatorSet等。

    属性动画作用属性

    1,属性动画可以对任意对象的属性进行动画而不仅仅是View,属性动画默认间隔300ms,默认帧率10ms/帧。
    2,看一段代码
    1. <set
    2. android:ordering=["together" | "sequentially"]>
    3.  
    4. <objectAnimator
    5. android:propertyName="string"
    6. android:duration="int"
    7. android:valueFrom="float | int | color"
    8. android:valueTo="float | int | color"
    9. android:startOffset="int"
    10. android:repeatCount="int"
    11. android:repeatMode=["repeat" | "reverse"]
    12. android:valueType=["intType" | "floatType"]/>
    13.  
    14. <animator
    15. android:duration="int"
    16. android:valueFrom="float | int | color"
    17. android:valueTo="float | int | color"
    18. android:startOffset="int"
    19. android:repeatCount="int"
    20. android:repeatMode=["repeat" | "reverse"]
    21. android:valueType=["intType" | "floatType"]/>
    22.  
    23. <set>
    24. ...
    25. </set>
    26. </set>

    <set>

    它代表的就是一个AnimatorSet对象。里面有一个 ordering属性,主要是指定动画的播放顺序。

    <objectAnimator> 
    它表示一个ObjectAnimator对象。它里面有很多属性,我们重点需要了解的也是它。
    android:propertyName -------属性名称,例如一个view对象的”alpha”和”backgroundColor”。
    android:valueFrom   --------变化开始值
    android:valueTo ------------变化结束值

    android:valueType -------变化值类型 ,它有两种值:intType和floatType,默认值floatType。
    android:duration ---------持续时间

    android:startOffset ---------动画开始延迟时间
    android:repeatCount --------重复次数,-1表示无限重复,默认为-1
    android:repeatMode 重复模式,前提是android:repeatCount为-1 ,它有两种值:”reverse”和”repeat”,分别表示反向和顺序方向。

    <animator>
    它对应的就是ValueAnimator对象。它主要有以下属性。
    android:valueFrom
    android:valueTo
    android:duration
    android:startOffset
    android:repeatCount
    android:repeatMode
    android:valueType

    定义了一组动画之后,我们怎么让它运行起来呢?
    1. AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    2. R.anim.property_animator);
    3. set.setTarget(myObject);//myObject表示作用的对象
    4. set.start();

    插值器和估值器

    时间插值器(TimeInterpolator)的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画),AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快),DecelerateInterpolator(减速插值器:动画越来越慢)。

    注:这里的插值器很多,可以翻看我之前关于插值器的讲解。

    估值器(TypeEvaluator)的作用是根据当前属性改变的百分比来计算改变后的属性值。系统预置有IntEvaluator 、FloatEvaluator 、ArgbEvaluator。

    举个简单的例子吧
    1. public class IntEvaluator implements TypeEvaluator<Integer> {
    2. public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
    3. int startInt = startValue;
    4. return (int)(startInt + fraction * (endValue - startInt));
    5. }
    6. }

    上述代码就是计算当前属性所占总共的百分百。


    插值器和估值器除了系统提供之外,我们还可以自定义实现,自定义插值器需要实现Interpolator或者TimeInterpolator;自定义估值器算法需要实现TypeEvaluator。

    属性动画监听器

    属性动画监听器用于监听动画的播放过程,主要有两个接口:AnimatorUpdateListener和AnimatorListener 。
    AnimatorListener 
    1. public static interface AnimatorListener {
    2. void onAnimationStart(Animator animation); //动画开始
    3. void onAnimationEnd(Animator animation); //动画结束
    4. void onAnimationCancel(Animator animation); //动画取消
    5. void onAnimationRepeat(Animator animation); //动画重复播放
    6. }

    AnimatorUpdateListener

    1. public static interface AnimatorUpdateListener {
    2. void onAnimationUpdate(ValueAnimator animator);
    3. }

    应用场景


    这里我们先提一个问题:给Button加一个动画,让Button在2秒内将宽带从当前宽度增加到500dp,也行你会说,很简单啊,直接用view动画就可以实现,view动画不是有个缩放动画,但是你可以试试,view动画是不支持对宽度和高度进行改变的。Button继承自TextView,setWidth是对TextView的,所以直接对Button做setWidth是不行的。那么要怎么做呢?
    针对上面的问题,官网api给出了如下的方案:
    • 给你的对象加上get和set方法,如果你有权限的话
    • 用一个类来包装原始对象,间接提高get和set方法
    • 采用ValueAnimator,监听动画执行过程,实现属性的改变

    有了上面的说明,我们大致明白了,要实现开始说的这个问题的效果,我们需要用一个间接的类来实现get和set方法或者自己实现一个ValueAnimator。

    第一种,自己封装一个类实现get和set方法,这也是我们常用的,拓展性强

    1. public class ViewWrapper {
    2. private View target;
    3. public ViewWrapper(View target) {
    4. this.target = target;
    5. }
    6. public int getWidth() {
    7. return target.getLayoutParams().width;
    8. }
    9. public void setWidth(int width) {
    10. target.getLayoutParams().width = width;
    11. target.requestLayout();
    12. }
    13. }

    第二种,采用ValueAnimator,监听动画过程。


    1. private void startValueAnimator(final View target, final int start, final int end) {
    2. ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
    3. valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    4. private IntEvaluator mEvaluation = new IntEvaluator();//新建一个整形估值器作为临时变量
    5.  
    6. @Override
    7. public void onAnimationUpdate(ValueAnimator animation) {
    8. //获得当前动画的进度值 1~100之间
    9. int currentValue = (int) animation.getAnimatedValue();
    10. //获得当前进度占整个动画过程的比例,浮点型,0~1之间
    11. float fraction = animation.getAnimatedFraction();
    12. //调用估值器,通过比例计算出宽度
    13. int targetWidth = mEvaluation.evaluate(fraction, start, end);
    14. target.getLayoutParams().width = targetWidth;
    15. //设置给作用的对象,刷新页面
    16. target.requestLayout();
    17. }
    18. });
    19. }

    属性动画的工作原理

    属性动画的工作原理,主要是对作用的对象不断的调用get/set方法来改变初始值和最终值,然后set到动画属性上即可。然后通过消息机制(Handler(不过这里的Handler不是我们常用的handler,而是AnimationHandler,它其实本质就是一个Runable)和Looper去将动画执行出来),通过代码我们发现它调了JNI的代码,不过这个我们不用关心,我们直接看ObjectAnimator.start()
    1. private void start(boolean playBackwards) {
    2. if(Looper.myLooper() == null) {
    3. throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    4. } else {
    5. this.mPlayingBackwards = playBackwards;
    6. this.mCurrentIteration = 0;
    7. this.mPlayingState = 0;
    8. this.mStarted = true;
    9. this.mStartedDelay = false;
    10. ((ArrayList)sPendingAnimations.get()).add(this);
    11. if(this.mStartDelay == 0L) {
    12. this.setCurrentPlayTime(this.getCurrentPlayTime());
    13. this.mPlayingState = 0;
    14. this.mRunning = true;
    15. if(this.mListeners != null) {
    16. ArrayList animationHandler = (ArrayList)this.mListeners.clone();
    17. int numListeners = animationHandler.size();
    18.  
    19. for(int i = 0; i < numListeners; ++i) {
    20. ((AnimatorListener)animationHandler.get(i)).onAnimationStart(this);
    21. }
    22. }
    23. }
    24.  
    25. ValueAnimator.AnimationHandler var5 = (ValueAnimator.AnimationHandler)sAnimationHandler.get();
    26. if(var5 == null) {
    27. var5 = new ValueAnimator.AnimationHandler(null);
    28. sAnimationHandler.set(var5);
    29. }
    30.  
    31. var5.sendEmptyMessage(0);
    32. }
    33. }

    1. private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations = new ThreadLocal() {
    2. protected ArrayList<ValueAnimator> initialValue() {
    3. return new ArrayList();
    4. }
    5. };
    6. private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations = new ThreadLocal() {
    7. protected ArrayList<ValueAnimator> initialValue() {
    8. return new ArrayList();
    9. }
    10. };
    11. private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims = new ThreadLocal() {
    12. protected ArrayList<ValueAnimator> initialValue() {
    13. return new ArrayList();
    14. }
    15. };
    16. private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims = new ThreadLocal() {
    17. protected ArrayList<ValueAnimator> initialValue() {
    18. return new ArrayList();
    19. }
    20. };
    21. private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims = new ThreadLocal() {
    22. protected ArrayList<ValueAnimator> initialValue() {
    23. return new ArrayList();
    24. }
    25. };

    这里系统怎么计算每一帧的动画的呢,看看下面的代码

    1. void animateValue(float fraction) {
    2. fraction = this.mInterpolator.getInterpolation(fraction);
    3. this.mCurrentFraction = fraction;
    4. int numValues = this.mValues.length;
    5.  
    6. int numListeners;
    7. for(numListeners = 0; numListeners < numValues; ++numListeners) {
    8. this.mValues[numListeners].calculateValue(fraction);
    9. }
    10.  
    11. if(this.mUpdateListeners != null) {
    12. numListeners = this.mUpdateListeners.size();
    13.  
    14. for(int i = 0; i < numListeners; ++i) {
    15. ((ValueAnimator.AnimatorUpdateListener)this.mUpdateListeners.get(i)).onAnimationUpdate(this);
    16. }
    17. }
    18.  
    19. }

    不过我们知道要改变动画,一定调用了get/set方法,那我们重点看下这相关的代码。这段代码在setProperty方法里面

    1. public void setProperty(Property property) {
    2. if(this.mValues != null) {
    3. PropertyValuesHolder valuesHolder = this.mValues[0];
    4. String oldName = valuesHolder.getPropertyName();
    5. valuesHolder.setProperty(property);
    6. this.mValuesMap.remove(oldName);
    7. this.mValuesMap.put(this.mPropertyName, valuesHolder);
    8. }
    9.  
    10. if(this.mProperty != null) {
    11. this.mPropertyName = property.getName();
    12. }
    13.  
    14. this.mProperty = property;
    15. this.mInitialized = false;
    16. }

    这里有一个PropertyValuesHolder,顾名思义这是一个操作数据的类,和我们的adapter的Holder差不多,该方法的get方法主要用到了反射。

    1. private void setupValue(Object target, Keyframe kf) {
    2. if(this.mProperty != null) {
    3. kf.setValue(this.mProperty.get(target));
    4. }
    5.  
    6. try {
    7. if(this.mGetter == null) {
    8. Class e = target.getClass();
    9. this.setupGetter(e);
    10. }
    11.  
    12. kf.setValue(this.mGetter.invoke(target, new Object[0]));
    13. } catch (InvocationTargetException var4) {
    14. Log.e("PropertyValuesHolder", var4.toString());
    15. } catch (IllegalAccessException var5) {
    16. Log.e("PropertyValuesHolder", var5.toString());
    17. }
    18.  
    19. }

    代码就看到这,有兴趣的可以去看下源码


    使用属性动画需要注意的事项

  • 使用帧动画时,当图片数量较多且图片分辨率较大的时候容易出现OOM,需注意,尽量避免使用帧动画。
  • 使用无限循环的属性动画时,在Activity退出时即使停止,否则将导致Activity无法释放从而造成内存泄露
  • View动画是对View的影像做动画,并不是真正的改变了View的状态,因此有时候会出现动画完成后View无法隐藏(setVisibility(View.GONE)失效),这时候调用view.clearAnimation()清理View动画即可解决。
  • 不要使用px,使用px会导致不同设备上有不同的效果。
  • View动画是对View的影像做动画,View的真实位置没有变动,也就导致点击View动画后的位置触摸事件不会响应,属性动画不存在这个问题。
  • 使用动画的过程中,使用硬件加速可以提高动画的流畅度。
  • 动画在3.0以下的系统存在兼容性问题,特殊场景可能无法正常工作,需做好适配工作。





  • Android动画深入分析的更多相关文章

    1. 《Android开发艺术探索》读书笔记 (7) 第7章 Android动画深入分析

      本节和<Android群英传>中的第七章Android动画机制与使用技巧有关系,建议先阅读该章的总结 第7章 Android动画深入分析 7.1 View动画 (1)android动画分为 ...

    2. Android 动画深入分析

      一些娱乐动画安德鲁斯被广泛使用应用上述的.在不牺牲性能,它可以带来非常好的体验,下面会解释具体的实现安卓动画.知识的学校一个明确清晰的白色. 动画类型 Android的animation由四种类型组成 ...

    3. 第七章:Android动画深入分析

      7.1 View动画 View动画的作用对象是View,它支持四种动画效果,分别是平移动画,缩放动画,旋转动画和透明动画. 帧动画也属于View动画,但是帧动画的表现形式和上面的四种变换效果不太一样. ...

    4. Android 动画animation 深入分析

      转载请注明出处:http://blog.csdn.net/farmer_cc/article/details/18259117 Android 动画animation 深入分析 前言:本文试图通过分析 ...

    5. 深入分析Android动画(二)

      上回书说到Android动画的分类以及基本使用,这会书主要说Android属性动画的原理,对于View动画的原理本篇不做深入分析.对于Android动画的基础请看深入分析Android动画(一) 我们 ...

    6. 深入分析Android动画(一)

      动画的分类: ①View动画 View动画顾名思义其作用对象为View,包含平移.缩放.旋转.透明,这四类变化分别对应着Animation的子类TranlateAnimation.ScaleAnima ...

    7. Android动画效果之自定义ViewGroup添加布局动画

      前言: 前面几篇文章介绍了补间动画.逐帧动画.属性动画,大部分都是针对View来实现的动画,那么该如何为了一个ViewGroup添加动画呢?今天结合自定义ViewGroup来学习一下布局动画.本文将通 ...

    8. Android动画效果之Property Animation进阶(属性动画)

      前言: 前面初步认识了Android的Property Animation(属性动画)Android动画效果之初识Property Animation(属性动画)(三),并且利用属性动画简单了补间动画 ...

    9. Android动画效果之初识Property Animation(属性动画)

      前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

    随机推荐

    1. jQuery extend 方法使用 (转)

      方法介绍 jQuery 的 API 手册中,extend 方法挂载在 jQuery 和 jQuery.fn 两个不同的对象上,但在 jQuery 内部代码实现的是相同的,只是功能各不相同. 先看看官方 ...

    2. Socket.io应用之联网拖拽游戏

      服务器端代码: const express=require('express'); const http=require('http'); const sio=require('socket.io') ...

    3. 【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群

      http://www.csdn.net/article/2014-11-06/2822529/3 大规模流量的网站架构,从来都是慢慢"成长"而来.而这个过程中,会遇到很多问题,在不 ...

    4. 未能加载 global.asax的类的解决方案

      “/suitecallback”应用程序中的服务器错误. 分析器错误 说明: 在分析向此请求提供服务所需资源时出错.请检查下列特定分析错误详细信息并适当地修改源文件. 分析器错误消息: 未能加载类型“ ...

    5. 无忧代理免费ip爬取(端口js加密)

      起因 为了训练爬虫技能(其实主要还是js技能-),翻了可能有反爬的网站挨个摧残,现在轮到这个网站了:http://www.data5u.com/free/index.shtml 解密过程 打开网站,在 ...

    6. jQuery 效果 – 滑动

      jQuery 滑动方法可使元素上下滑动. 点击这里,隐藏/显示面板 一寸光阴一寸金,因此,我们为您提供快捷易懂的学习内容. 在这里,您可以通过一种易懂的便利的模式获得您需要的任何知识. 实例 jQue ...

    7. JavaScript Boolean(布尔)对象

      Boolean(布尔)对象用于将非布尔值转换为布尔值(true 或者 false). Boolean(布尔)对象是三种包装对象:Number.String和Boolean中最简单的一种,它没有大量的实 ...

    8. Android 学习笔记一 自定义按钮背景图

      入门学到的一些组件都是比较规矩的,但在实际应用中,我们需要更多特色的组件,例如一个简单的Button,所以我们必须要自定义它的属性. 遇到的问题:用两张图片来代替按钮,分别表示点击前后 解决方法:用I ...

    9. 浏览器加载和渲染html的顺序(html/css/js)

      最近在学习前端的技术,把html.js.css的基础知识看了看.感觉越看越觉得前端并不比后端容易,技术含量还是相当大的.今天突然想弄明白浏览器到底是怎么加载和渲染html的?html中的DOM.js文 ...

    10. linux TCP数据包封装在SKB的过程分析

      在linux中 tcp的数据包的封装是在函数tcp_sendmsg开始的,在函数tcp_sendmsg中用到skb = sk_stream_alloc_skb(sk, select_size(sk, ...