`
xiangzhihong
  • 浏览: 9227 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Android动画分析

 
阅读更多

动画分类

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动画。
代码实现:
[html] view plain copy
 
  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
[html] view plain copy
 
  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"/>  
第二种,代码种使用
[html] view plain copy
 
  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来实现帧动画,帧动画比较简单,我们看一个例子就行了。
[html] view plain copy
 
  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>  

然后
[html] view plain copy
 
  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,看一段代码
[html] view plain copy
 
  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
 
定义了一组动画之后,我们怎么让它运行起来呢?
[html] view plain copy
 
  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。
 
举个简单的例子吧
[html] view plain copy
 
  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 
[html] view plain copy
 
  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
[html] view plain copy
 
  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方法,这也是我们常用的,拓展性强
 
[html] view plain copy
 
  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,监听动画过程。
 
[html] view plain copy
 
  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()
[html] view plain copy
 
  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.     }  
 
[html] view plain copy
 
  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.    };  

这里系统怎么计算每一帧的动画的呢,看看下面的代码
[html] view plain copy
 
  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方法里面
[html] view plain copy
 
  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方法主要用到了反射。
[html] view plain copy
 
  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 动画分析

    ### Android 动画分析 #### 一、Android动画概述 Android平台提供了丰富的动画支持,使得开发者可以轻松地创建各种复杂的视觉效果。动画是通过改变视图(View)的状态来实现的,这些状态包括透明度、尺寸、位置...

    ANDROID开机动画分析

    在Android系统中,开机动画不仅是一段短暂的视觉体验,更是系统启动过程中的一个重要环节。这篇文章将深入探讨ANDROID开机动画的分析,涉及到的知识点包括Android系统的启动流程、动画的制作原理以及如何通过源码和...

    Android动画测试源码

    在Android开发中,动画是提升用户体验的关键因素,它能让应用变得更加生动、有趣。这份"Android动画测试源码"提供了一种深入理解Android动画机制的方式,通过实际的代码示例进行学习和测试。以下是对相关知识点的...

    android属性动画(伸展收缩)

    在Android开发中,属性动画(Property Animation)是一个强大的工具,用于创建动态效果和交互,它使得对象可以在多个帧之间平滑地改变其属性。在这个"android属性动画(伸展收缩)"的示例中,我们将深入探讨如何利用...

    Android 动画机制 补间、属性、帧动画、源码分析

    Android 动画机制 补间、属性、帧动画、源码分析 Android系统提供了很多丰富的API去实现UI的2D与3D动画,最主要的划分可以分为如下几类: * View Animation:最早提供的一种动画,用来这只view。 * Drawable ...

    android path 动画效果 (很酷)

    标题中的"android path 动画效果 (很酷)"指的就是使用Android的动画系统来实现路径动画,使得UI元素能够沿着预定义的路径移动,产生流畅且酷炫的视觉效果。以下将详细介绍这一技术。 首先,Path Animation是Android...

    android3.0及以上动画分析

    本篇文章将深入探讨Android 3.0及以后版本中的动画系统,包括核心概念、API使用以及源码分析。 首先,Android 3.0引入了两种主要的动画类型:属性动画(Property Animation)和视图动画(View Animation)。视图...

    Android按下录音录音动画效果 ,自定义录音、播放动画View

    自定义视图在Android中通常是通过继承`View`或`ViewGroup`类并重写其`onDraw()`方法来实现的。在这个`SoundRecordView`中,我们需要绘制一个可随录音时间增长而变化的圆环动画。 1. **绘制圆环**:使用`Canvas`对象...

    数百种Android动画效果源码

    8. **毕业论文参考**:对于学习Android开发的学生,这个项目可以作为毕业论文的实践部分,研究不同动画的实现原理,分析其性能差异,并提出改进方案。 9. **完整项目实践**:这个项目作为一个完整的Android应用,...

    Android动画自定义源码分析

    这些动画类型涵盖了Android中常见的几种动画效果,如透明度变化、大小缩放、旋转以及位置移动等。 ##### 1.2 `startAnimation()`方法 当我们要在View上应用动画时,通常会调用`startAnimation()`方法。该方法的...

    Android动画框架详解

    Android 平台提供了一套完整的...本文是第一部分原理篇,主要分析 Tween 动画的实现原理, 最后简单介绍在 Android 中如何通过播放 Gif 文件来实现动画。第二部分实例篇将在原理篇的基础上,向您展示一个动画实例的实现

    android动画-雪花飘落demo

    这个Demo旨在帮助开发者学习和理解Android中的动画机制,特别是对于视图(View)动画和属性动画(Attribute Animation)的应用。 在Android中,有两种主要的动画类型:帧动画(Frame Animation)和属性动画。帧动画适用于...

    Android-Android密码动画

    在Android中实现密码动画,通常会用到以下技术: 1. **自定义View**:为了实现特定的动画效果,开发者可能需要创建自定义的`View`类。在这个自定义的`View`中,可以重写`onDraw()`方法来绘制每个密码字符的点或星号...

    Android 动画之属性动画ValueAnimator

    在Android开发中,动画是提升用户体验的关键因素之一。属性动画(Property Animation)系统是Android 3.0(API级别11)引入的一个重要特性,它极大地扩展了Android平台上的动画能力。ValueAnimator作为属性动画系统...

    Android 动画 Animation Demo

    在Android开发中,动画(Animation)是提升用户体验和界面交互性的重要工具。Android动画主要分为两种类型:属性动画(Property Animation)和视图动画(View Animation)。本篇将深入探讨这两种动画机制,以及如何在...

    Android动画学习总结---下

    在Android开发中,动画是提升用户体验的关键因素,它能让应用变得更加生动、有趣。这篇"Android动画学习总结---下"着重探讨了Android属性动画(Property Animation)的使用方法。属性动画系统是Android 3.0(API ...

    android游戏开场动画源代码

    本篇文章将详细解析`android游戏开场动画源代码`,并围绕`android 游戏源代码`、`android 游戏`以及`android动画`这三大标签展开讨论。 首先,我们来探讨一下Android游戏源代码。源代码是游戏的灵魂,它包含了游戏...

    Android属性动画超全超详细

    通过分析这些文件,我们可以学习如何在实际项目中设置和运行属性动画,包括导入必要的依赖、配置构建脚本、创建动画实例以及将动画应用到UI元素上。这个示例项目是一个宝贵的资源,可以帮助开发者深入理解Android...

    Android 动画顺序播放源码.zip

    在Android中,动画主要分为两种类型:补间动画(Tween Animation)和帧动画(Frame Animation)。补间动画用于改变视图的位置、大小、透明度等属性,而帧动画则是通过显示一系列静态图像来创建连续运动的效果。 1. ...

    Android 动画 思维导图

    这是一个详细的分析android 动画的思维导图,从视图动画到属性动画,都包含有,并且有详细的备注,帮助大家完善动画体系

Global site tag (gtag.js) - Google Analytics