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动画。

    代码实现:

    <?xml version="1.0" encoding="utf-8"?>
    <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
     android:animationOrder="normal"
     android:delay="0.3" android:animation="@anim/anim_item"/>
    
    //--- animationOrder 表示子元素的动画的顺序,有三种选项:
    //normal(顺序显示)、reverse(逆序显示)和random(随机显示)。
    
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="300"
     android:shareInterpolator="true">
     <alpha
         android:fromAlpha="0.0"
         android:toAlpha="1.0" />
     <translate
         android:fromXDelta="300"
         android:toXDelta="0" />
    </set>

    第一种,在布局中引用LayoutAnimation

    <ListView
         android:id="@+id/lv"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
         android:layoutAnimation="@anim/anim_layout"/>

    第二种,代码种使用

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

    帧动画

    逐帧动画(Frame-by-frame Animations)从字面上理解就是一帧挨着一帧的播放图片,类似于播放电影的效果。不同于View动画,Android系统提供了一个类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="@mipmap/lottery_1"
            android:duration="200" />
      // ...省略很多
        <item
            android:drawable="@mipmap/lottery_6"
            android:duration="200" />
    
    </animation-list>

    然后

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

    属性动画

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

    ValueAnimator、ObjectAnimator、AnimatorSet等。

    属性动画作用属性

    1,属性动画可以对任意对象的属性进行动画而不仅仅是View,属性动画默认间隔300ms,默认帧率10ms/帧。

    2,看一段代码

    <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对象。里面有一个 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

    定义了一组动画之后,我们怎么让它运行起来呢?

    AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
        R.anim.property_animator);
    set.setTarget(myObject);//myObject表示作用的对象
    set.start();

    插值器和估值器

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

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

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

    举个简单的例子吧

    public class IntEvaluator implements TypeEvaluator<Integer> {
     public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
         int startInt = startValue;
         return (int)(startInt + fraction * (endValue - startInt));
     }
    }

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

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

    属性动画监听器

    属性动画监听器用于监听动画的播放过程,主要有两个接口:AnimatorUpdateListener和AnimatorListener 。

    AnimatorListener

    public static interface AnimatorListener {
        void onAnimationStart(Animator animation); //动画开始
        void onAnimationEnd(Animator animation); //动画结束
        void onAnimationCancel(Animator animation); //动画取消
        void onAnimationRepeat(Animator animation); //动画重复播放
    }

    AnimatorUpdateListener

    public static interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animator);
    }

    应用场景

    这里我们先提一个问题:给Button加一个动画,让Button在2秒内将宽带从当前宽度增加到500dp,也行你会说,很简单啊,直接用view动画就可以实现,view动画不是有个缩放动画,但是你可以试试,view动画是不支持对宽度和高度进行改变的。Button继承自TextView,setWidth是对TextView的,所以直接对Button做setWidth是不行的。那么要怎么做呢?

    针对上面的问题,官网api给出了如下的方案:

    • 给你的对象加上get和set方法,如果你有权限的话
    • 用一个类来包装原始对象,间接提高get和set方法
    • 采用ValueAnimator,监听动画执行过程,实现属性的改变

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

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

    public class ViewWrapper {
     private View target;
     public ViewWrapper(View target) {
         this.target = target;
     }
     public int getWidth() {
         return target.getLayoutParams().width;
     }
     public void setWidth(int width) {
         target.getLayoutParams().width = width;
         target.requestLayout();
     }
    }

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

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

    属性动画的工作原理

    属性动画的工作原理,主要是对作用的对象不断的调用get/set方法来改变初始值和最终值,然后set到动画属性上即可。然后通过消息机制(Handler(不过这里的Handler不是我们常用的handler,而是AnimationHandler,它其实本质就是一个Runable)和Looper去将动画执行出来),通过代码我们发现它调了JNI的代码,不过这个我们不用关心,我们直接看ObjectAnimator.start()

    private void start(boolean playBackwards) {
            if(Looper.myLooper() == null) {
                throw new AndroidRuntimeException("Animators may only be run on Looper threads");
            } else {
                this.mPlayingBackwards = playBackwards;
                this.mCurrentIteration = 0;
                this.mPlayingState = 0;
                this.mStarted = true;
                this.mStartedDelay = false;
                ((ArrayList)sPendingAnimations.get()).add(this);
                if(this.mStartDelay == 0L) {
                    this.setCurrentPlayTime(this.getCurrentPlayTime());
                    this.mPlayingState = 0;
                    this.mRunning = true;
                    if(this.mListeners != null) {
                        ArrayList animationHandler = (ArrayList)this.mListeners.clone();
                        int numListeners = animationHandler.size();
    
                        for(int i = 0; i < numListeners; ++i) {
                            ((AnimatorListener)animationHandler.get(i)).onAnimationStart(this);
                        }
                    }
                }
    
                ValueAnimator.AnimationHandler var5 = (ValueAnimator.AnimationHandler)sAnimationHandler.get();
                if(var5 == null) {
                    var5 = new ValueAnimator.AnimationHandler(null);
                    sAnimationHandler.set(var5);
                }
    
                var5.sendEmptyMessage(0);
            }
        }
     private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations = new ThreadLocal() {
            protected ArrayList<ValueAnimator> initialValue() {
                return new ArrayList();
            }
        };
        private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations = new ThreadLocal() {
            protected ArrayList<ValueAnimator> initialValue() {
                return new ArrayList();
            }
        };
        private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims = new ThreadLocal() {
            protected ArrayList<ValueAnimator> initialValue() {
                return new ArrayList();
            }
        };
        private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims = new ThreadLocal() {
            protected ArrayList<ValueAnimator> initialValue() {
                return new ArrayList();
            }
        };
        private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims = new ThreadLocal() {
            protected ArrayList<ValueAnimator> initialValue() {
                return new ArrayList();
            }
        };

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

    void animateValue(float fraction) {
            fraction = this.mInterpolator.getInterpolation(fraction);
            this.mCurrentFraction = fraction;
            int numValues = this.mValues.length;
    
            int numListeners;
            for(numListeners = 0; numListeners < numValues; ++numListeners) {
                this.mValues[numListeners].calculateValue(fraction);
            }
    
            if(this.mUpdateListeners != null) {
                numListeners = this.mUpdateListeners.size();
    
                for(int i = 0; i < numListeners; ++i) {
                    ((ValueAnimator.AnimatorUpdateListener)this.mUpdateListeners.get(i)).onAnimationUpdate(this);
                }
            }
    
        }

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

     public void setProperty(Property property) {
            if(this.mValues != null) {
                PropertyValuesHolder valuesHolder = this.mValues[0];
                String oldName = valuesHolder.getPropertyName();
                valuesHolder.setProperty(property);
                this.mValuesMap.remove(oldName);
                this.mValuesMap.put(this.mPropertyName, valuesHolder);
            }
    
            if(this.mProperty != null) {
                this.mPropertyName = property.getName();
            }
    
            this.mProperty = property;
            this.mInitialized = false;
        }

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

     private void setupValue(Object target, Keyframe kf) {
            if(this.mProperty != null) {
                kf.setValue(this.mProperty.get(target));
            }
    
            try {
                if(this.mGetter == null) {
                    Class e = target.getClass();
                    this.setupGetter(e);
                }
    
                kf.setValue(this.mGetter.invoke(target, new Object[0]));
            } catch (InvocationTargetException var4) {
                Log.e("PropertyValuesHolder", var4.toString());
            } catch (IllegalAccessException var5) {
                Log.e("PropertyValuesHolder", var5.toString());
            }
    
        }

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

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

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

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

    Android开发艺术探索——第七章:Android动画深入分析

    Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

    Android 动画animation 深入分析

    转载请注明出处:http://blog.csdn.net/farmer_cc/article/details/18259117 Android 动画animation 深入分析 前言:本文试图通过分析动画流程,来理解android动画系统的设计与实现,学习动画的基本原则,最终希望能够指导动画的设计. 0 本文中用到的一些类图 1 view animation 调用方法:view.startAnimation(animation); [java] view plaincopy public voi

    android动画具体解释六 XML中定义动画

    动画View 属性动画系统同意动画View对象并提供非常多比view动画系统更高级的功能.view动画系统通过改变绘制方式来变换View对象,view动画是被view的容器所处理的,由于View本身没有要操控的属性.结果就是View被动画了.但View对象本身并没有变化. 在Android3.0中,新的属性和对应的getter和setter方法被增加以克服此缺点. 属性动画系统能够通过改变View对象的真实属性来动画Views. 并且.View也会在其属性改变时自己主动调用invalidate(

    android 动画(1) 补间动画

    android动画: 3.0以前,android支持两种动画模式,tween animation,frame animation, 3.0中又引入了一个新的动画系统:property animation, 这三种动画模式在SDK中被称为 property animation,        属性动画: view animation,   补间动画:  给出两个关键帧,通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变. (Tween animation) drawable animatio

    android动画详解三 动画API概述

    · 属性动画与view动画的不同之处 view动画系统提供了仅动画View 对象的能力,所以如果你想动画非View 对象,你就要自己实现代码. view动画系统实际上还被强制仅能对 View 的少数属性进行动画,比如缩放和旋转,而不能对背景色进行. view动画的另一个坏处是它仅修改View的绘制位置,而不是View的实际位置.例如,如果你动画一个移动穿越屏幕,button的绘制位置是正确的,但实际你可以点击它的位置却没有变,所以你必须去实现你自己的逻辑来处理它. 使用属性动画系统时,这个限制被

    Android动画基础

    Android动画主要有三种: 1> 视图动画,也叫Tween(补间)动画可以在一个视图容器内执行一系列简单变换(位置.大小.旋转.透明度).譬如,如果你有一个TextView对象,您可以移动.旋转.缩放.透明度设置其文本,当然,如果它有一个背景图像,背景图像会随着文本变化. 补间动画通过XML或Android代码定义,建议使用XML文件定义,因为它更具可读性.可重用性. 2> Drawable动画其实就是Frame动画(帧动画),它允许你实现像播放幻灯片一样的效果,这种动画的实质其实是Dra

    Android动画效果——1.帧动画2.补间动画3.跳转画面(三)

    Android--动画效果1.帧动画2.补间动画3.跳转画面 插值器类 xml属性值 说明 LinearInterpolator @android:anim/linear_interpolatorr 动画以均匀的速度改变. AccelerateInterpolator @android:anim/accelerate_interpolator 在动画开始时改变速度较慢,然后开始加速. AccelerateDecelerateInterpolator @android:anim/accelerat

    Android动画AnimationSet遇到的问题。

    之前对Android动画这块一直是一知半解,知道个大概,并不会使用.刚好这几天没有太多的任务要做,可以梳理一下Android动画的一些知识.Android Animation的基础用法就不说了,这里主要记录下简单实用中遇到的问题. 1.XML中AnimationSet的某些属性有些问题. 主要就是android:repeatCount,android:repeatMode无效.这个问题据说是Google的工程师刻意为之.[参考:http://stackoverflow.com/questions

    Android 动画详解

    这次主要就介绍android动画,android动画目前分为三种形式,Tween Animation 这个只能应用于view对象上面的,Drawable Animation这个是帧动画,就是类似我们有一些列的图片依次播放图片时出现的动画,Property Animation 这个是属性动画,这也是在android3.0之后引进的动画,在手机的版本上是android4.0就可以使用这个动画,下面我们主要就是针对这三种情况进行介绍. Tween Animation 这个动画在Property Ani