美文网首页
Android动画总结

Android动画总结

作者: 留给时光吧 | 来源:发表于2018-05-04 14:45 被阅读0次

    Android中的动画大致上分为视图动画(View Animation)和属性动画(Property Animation)两种,其中视图动画又分为补间动画(Tween Animation)和帧动画两种(Frame Animation)。其中每种动画有分很多情况,虽然用着都比较简单,但是经常容易忘,所以这里做一个总结。

    1.帧动画

    基本上属于最简单的一种动画,主要就是像幻灯片一样播放一组图片,形成动画效果。如下图一个进度条的动画就是许多张图片依次播放的效果:


    帧动画有两种做法,一种利用xml文件,一种利用java代码。

    1.1 xml文件写法

    首先在res/drawable目录下创建一个animation-list类型的文件,虽然是动画文件(虽然是帧动画的这个文件再Android Studio中不能放在anim文件夹下)内容如下:

    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot="false">
        <item
            android:drawable="@drawable/indeterminate01"
            android:duration="50" />
        <item
            android:drawable="@drawable/indeterminate02"
            android:duration="50" />
        <item
            android:drawable="@drawable/indeterminate03"
            android:duration="50" />
        <item
            android:drawable="@drawable/indeterminate04"
            android:duration="50" />
        <item
            android:drawable="@drawable/indeterminate05"
            android:duration="50" />
        <item
            android:drawable="@drawable/indeterminate06"
            android:duration="50" />
    </animation-list>
    

    内容很简单,就是按照顺序将一张一张图片列出来,duration属性表示这张图片的暂留时间,oneshot表示是否播放一次就结束,循环播放的话用false。

    帧动画的载体是ImageView,当然也可以是别的控件,如TextView等可以设置图片显示的控件,因为我们前面写的那个xml文件是作为一个Drawable使用的,也就是为什么要放在drawable目录下的。不过为了最佳的显示效果还是用ImageView。如下,设置为ImageView的src

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/anim"
            android:src="@drawable/indeterminate"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    

    然后在代码中启动动画

    ImageView iv = findViewById(R.id.anim);
    AnimationDrawable drawable = (AnimationDrawable) iv.getDrawable();
    drawable.start();
    

    1.2 Java代码写法

    使用xml文件的好处是将UI逻辑从代码中剥离,而且xml文件看起来条例比较清晰,当然如果我们需要动态控制的时候,还是需要用java代码:

    ImageView iv = findViewById(R.id.anim);
    AnimationDrawable drawable = new AnimationDrawable();
    for (int i = 1; i < 7; i++)
            drawable.addFrame(getDrawable(getResources().getIdentifier("indeterminate0"+i,"drawable",getPackageName())),50);
    drawable.setOneShot(false);
    iv.setImageDrawable(drawable);
    drawable.start();
    

    addFrame:添加一帧图片及设置显示时间
    setOneShot:设置是否仅播放一次
    start() :开始播放
    stop() :停止播放
    isRunning(): 是否在播放

    2.视图动画

    一般而言视图动画分以下四种类型:平移,缩放,旋转,透明度变化。

    分别对应Java中以下4个类:TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation。

    在xml文件中体现为下面4个标签:<translate/>,<scale/>,<rotate/>,<alpha/>

    当然几种动画也可以组合起来使用,对于AnimationSet类和<set/>。

    一般建议在xml文件定义动画,动画文件存放在res/anim文件夹下,各种类型动画举例及属性说明如下:

    2.1 平移动画

    <translate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="5000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="500"
        android:toYDelta="0"
        />
    

    android:fromXDelta / android:fromYDelta: 起始X,Y坐标
    android:toXDelta / android:toYXDelta:结束X,Y坐标

    2.2 缩放动画

    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromXScale="0"
        android:fromYScale="0"
        android:toXScale="2"
        android:toYScale="2"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="3000"
        />
    

    android:fromXScale / android:fromYScale :起始XY轴缩放倍数
    android:toXScale / android:toYScale:最终XY缩放倍数
    android:pivotX / android:pivotY:缩放中心点位置,50%代表中间

    2.3 旋转动画

    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="3000"
        android:fromDegrees="0"
        android:toDegrees="300"
        android:pivotX="50%"
        android:pivotY="50%"
        />
    

    android:fromDegrees:旋转起始度数
    android:toDegrees:旋转结束度数
    android:pivotX / android:pivotY:旋转中心

    2.4 透明度变化

    <alpha xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="3000"
        android:fromAlpha="1"
        android:toAlpha="0"
        />
    

    android:fromAlpha:起始透明度,1为不透明,0为全透明
    android:toAlpha:动画结束时透明度

    2.5组合动画

    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="3000"
        android:shareInterpolator="true"
        >
        <rotate
            android:duration="3000"
            android:fromDegrees="0"
            android:toDegrees="359"
            android:pivotX="50%"
            android:pivotY="50%"
            />
        <scale
            android:fromXScale="0"
            android:fromYScale="0"
            android:toXScale="2"
            android:toYScale="2"
            android:pivotX="50%"
            android:pivotY="50%"
            />
    </set>
    

    android:shareInterpolator:表示组合动画中各个动画是否公用一个插值器

    2.6一些共有属性

    android:duration="3000" // 动画持续时间(ms)
    android:startOffset ="1000" // 动画延迟开始时间(ms)
    android:fillBefore = "true" // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
    android:fillAfter = "false" // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
    android:fillEnabled= "true"// 是否应用fillBefore值,对fillAfter值无影响,默认为true
    android:repeatMode= "restart" // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
    android:repeatCount = "0" // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
    android:interpolator = "anim/interpolator_resource" // 插值器
    

    2.7 动画的应用

    既然是view动画,就要实际应用到某个控件上,如下:

    ImageView iv = findViewById(R.id.anim);
    iv.startAnimation(AnimationUtils.loadAnimation(this,R.anim.set_anim));
    

    2.8 Java代码形式

    可以用xml书写的动画都可以用Java代码实现,便于动态修改,写起来也比较简单,只需实例化对应动画类型的对象,然后具体设置每种属性,简单例子如下:

    ImageView iv = findViewById(R.id.anim);
    ScaleAnimation scaleAnimation = new ScaleAnimation(0,0,2,2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    scaleAnimation.setDuration(3000);
    RotateAnimation rotateAnimation = new RotateAnimation(0,300,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    rotateAnimation.setDuration(3000);
    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(scaleAnimation);
    animationSet.addAnimation(rotateAnimation);
    iv.startAnimation(animationSet);
    

    2.9 动画监听

    animationSet.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
    

    2.10 自定义动画

    自定义动画仅需继承Animation类即可,然后重写applyTransformation和initialize两个方法。如下:

    protected void applyTransformation(float interpolatedTime, Transformation t) {
    }
    
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
    }
    

    initialize主要用于一些数据的初始化,applyTransformation则是动画中对view进行操作的实际地方,interpolatedTime表示动画进行的时间,从0到1递增,当值为1时,表示动画结束,Transformation 可以看成view的实体进行操作,通过getMatrix()可以获得变换矩阵,进行操作。动画的实现就是不停调用applyTransformation。

    一些复杂的自定义动画实现起来还是比较复杂的,我们可以看一下最简单的透明度动画的实现源码,关键代码如下:

        public AlphaAnimation(float fromAlpha, float toAlpha) {
            mFromAlpha = fromAlpha;
            mToAlpha = toAlpha;
        }
        
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            final float alpha = mFromAlpha;
            t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
        }
    

    可以看到还是很简单的,甚至没有重写initialize方法,仅在构造中保存了fromAlpha和toAlpha。然后在applyTransformation根据时间的变化设置相应的透明度。

    3.属性动画

    视图动画虽然简单易用,但是有很大的局限性,动画只能针对整个view,而且动画种类有限,可操作性较低,而且仅改变了视觉效果,别没有真正改变view的属性,所以如果需要更加复杂的动画效果,我们就需要使用属性动画。

    属性动画主要涉及ObjectAnimator、ValueAnimator、AnimatorSet等几个类,我们依次来学习:

    3.1 ValueAnimator

    ObjectAnimator是继承于ValueAnimator,所以我们先学习ValueAnimator。

    ValueAnimator实现动画的主要原理是不断变化数值,然后由我们赋给控件的各种属性从而实现动画效果,涉及的操作主要有ofInt,ofFloat,ofObject三个方法。

    3.1.1 ValueAnimator ofInt (int... values)

    使用这个方法主要是以整型进行数值变化,举例:

    final ImageView iv = findViewById(R.id.anim);
    ValueAnimator animator = ValueAnimator.ofInt(0,270).setDuration(3000);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int currentValue = (Integer) animation.getAnimatedValue();
                    iv.setRotation(currentValue);
                }
    });
    animator.start();
    

    在例子中我们首先设置变化范围为0~270,动画时间为3000ms,然后添加一个监听,主要是为了在每次数值发生变化时进行相应的操作,这里我们对view进行旋转操作。

    同样我们也可以在xml文件中定义动画:

    <animator xmlns:android="http://schemas.android.com/apk/res/android"
        android:valueFrom="0"
        android:valueTo="270"
        android:duration="3000"
        android:valueType="intType"
        />
    

    需要注意的时这里的xml文件要放在res/animator目录下,而且需要指定valueType,不指定的话默认为float。之后在java文件中进行应用:

    final ImageView iv = findViewById(R.id.anim);
    ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this,R.animator.anim);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int currentValue = (Integer) animation.getAnimatedValue();
                    iv.setRotation(currentValue);
                }
            });
    animator.start();
    

    效果和前面是一样的。

    3.1.2 ValueAnimator ofFloat (float... values)

    和ofInt基本一样,只不过过渡值变为float类型,这里不多叙述

    3.1.3 ValueAnimator ofObject (TypeEvaluator evaluator, Object... values)

    这个相当于是一个开放性的接口,前两个只能局限于固定的数据类型,这个方法我们可以指定任何类型的数据来实现各种动画。在这个方法中我们要指定一个自定义的TypeEvaluator和起始结束值,

    TypeEvaluator主要是为了计算动画过程中值变化的详细过程,如已经实现好的FloatEvaluator如下:

    public class FloatEvaluator implements TypeEvaluator<Number> {
       
        public Float evaluate(float fraction, Number startValue, Number endValue) {
            float startFloat = startValue.floatValue();
            return startFloat + fraction * (endValue.floatValue() - startFloat);
        }
    }
    

    可见实现起来也是很简单的,根据起始和终止值,不断乘以系数fraction 最后计算出每次变化的值。

    由此可见我们可以通过自定义所需TypeEvaluator,来实现各种对象的变化过程,最终来实现动画效果。

    3.1.4 其余属性

    当然和view动画类似,我们除了可以设置起始终止值和持续时间外,也可以设置如延迟,重复,插值器等属性,在XML文件中如下:

    android:startOffset ="1000" // 动画延迟开始时间(ms)
    android:fillBefore = "true"// 动画播放完后,视图是否会停留在动画开始的状态,默认为true
    android:fillAfter = "false"// 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
    android:fillEnabled= "true"// 是否应用fillBefore值,对fillAfter值无影响,默认为true
    android:repeatMode="restart"// 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
    android:repeatCount = "0"// 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
    android:interpolator =" @[package:]anim/interpolator_resource" // 插值器
    

    对应java代码中ValueAnimator类也有对应方法。

    3.2 ObjectAnimator

    ValueAnimator中主要是不断提供变化的值然后我们手动实现属性的变化,ObjectAnimator继承于ValueAnimator,对ValueAnimator进行了一定的简化。一些常用的操作,比如选择,渐变,我们每次都要加监听然后改变属性,比较繁琐。ObjectAnimator的出现可以让我们直接指定某些属性,然后划定起始终止值,然后一些交由系统完成。举例:

    ObjectAnimator animator = ObjectAnimator.ofFloat(view,"rotation",0,270);
    animator.setDuration(3000).start();
    

    可见和ValueAnimator及其类似,只不过要指定一个属性而已,调用start后自动对该属性进行变化。

    xml文件写法如下:

    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:valueFrom="0"
        android:valueTo="270"
        android:duration="3000"
        android:propertyName="rotation"
        />
    

    应用动画

    ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(this,R.animator.object_anim);
    animator.setTarget(view);
    animator.setDuration(3000).start();
    

    这个类使用的关键在于设置属性,一些基本的属性如下(图片来源于网络):



    需要注意的是指定属性时,对应的值要正确,如我们进行旋转时只能用ofFloat。

    当然一个控件的属性有很多,到底哪些属性可以通过ObjectAnimator 实现动画能,通过源码分析,那些有get,set方法的属性都是可以的,这也验证了ObjectAnimator 是继承于ValueAnimator,其实现原理都是一样的说法(都是不停地改变属性的值实现动画效果)。

    另外ObjectAnimator 和ValueAnimator一样也有一些共有属性可以设置,这里也不详细叙述了。

    最后通过文档我们可以知道ObjectAnimator 也有ofObject()方法,这样我们就可以通过包装或者直接添加get/set方法的形式,再配合自定义的估值器来实现各种动画效果。不过个人认为一些较复杂的效果还是ValueAnimator实现起来比较方便。

    3.3 AnimatorSet

    AnimatorSet 是用来实现组合动画的,本身并没有什么特殊效果,举例如下:

    ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
    ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.setDuration(3000);
    animatorSet.play(rotate).with(alpha);
    animatorSet.start();
    

    当然也可以用xml文件书写:

    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="together">
        <objectAnimator
            android:valueFrom="0"
            android:valueTo="1"
            android:propertyName="alpha"
            android:duration="3000"
            />
        <objectAnimator
            android:valueFrom="0"
            android:valueTo="360"
            android:propertyName="rotation"
            android:duration="3000"
            />
    </set>
    

    应用动画:

    AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.set_anim);
    animatorSet.setTarget(view);
    animatorSet.start();
    

    AnimatorSet 相比AnimationSet有一个顺序的概念。体现在java代码中就是涉及下面几个方法:

    AnimatorSet.play(Animator anim)   :播放当前动画
    AnimatorSet.after(long delay)   :将现有动画延迟x毫秒后执行
    AnimatorSet.with(Animator anim)   :将现有动画和传入的动画同时执行
    AnimatorSet.after(Animator anim)   :将现有动画插入到传入的动画之后执行
    AnimatorSet.before(Animator anim) :  将现有动画插入到传入的动画之前执行
    

    放在xml文件中,对应于set标签里的android:ordering属性,together表示标签内的动画同时执行,sequentially表示标签内的动画按次序执行。

    3.4 ViewPropertyAnimator

    这是一种更加简洁更加符合面向对象思想的动画。但其功能比较有限,基本和view动画的功能类似,但是这个属于属性动画。示例:

    view.animate().alpha(0).setDuration(3000).start();
    

    上面语句代表将透明度变为0的动画,实际应用中我们甚至不用调用start方法,系统会隐式的调用。

    组合动画也很简单:

    view.animate().alpha(0).rotation(270).setDuration(3000);
    

    另外ViewPropertyAnimator还有一套以By结尾的方法,如alphaBy()。区别是不带by的表示变化到某个值,带by的表示变化量为某个值。

    3.5 动画监听

    一般动画监听的方法如下:

    animator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
    
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            });
    

    有时我们仅仅只是需要知道开始或结束的时刻,不必实现这么多方法,可以用下面方法:

    animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                }
            });
    

    AnimatorListenerAdapter是个抽象类,实现了AnimatorListener接口,我们仅需实现所需的方法即可。

    4.插值器

    所有类型的动画都涉及到了interpolator这个属性,就是插值器。通过学习ValueAnimator我们了解到动画的本质就是不断改变数值,然后修改相应的属性。数值的改变也分很多种,如匀速改变,变速改变等,控制这种规律的就称为插值器。

    首先系统为我们内置了许多现成的插值器,可以满足我们大部分需要(图片来源于网络):


    当然我们也可以自定义插值器,满足各种特殊需求.一般而言我们首先要实现Interpolator接口,通过源码发现,Interpolator继承于TimeInterpolator,但没做任何处理,所以我们可以实现Interpolator接口或者TimeInterpolator接口。或者是和系统内置的插值器一样,继承BaseInterpolator类。

    不管如何继承或实现,最后都是在处理一个方法:

    float getInterpolation(float input);
    

    这个方法的参数input介于0和1之间,动画开始为0,结束为1,我们在这里对这个值进行加工后,就能按照我们的意愿得到各种动画改变规律。接下来我们看一下,系统内置的一些差值器的实现:

    public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
      
        ...
        public float getInterpolation(float input) {
            return input;
        }
    
        ...    
    }
    

    可见线性插值器没有做任何处理,怎么输入就怎么输出。

    public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
        private final float mFactor;
        private final double mDoubleFactor;
    
        public AccelerateInterpolator() {
            mFactor = 1.0f;
            mDoubleFactor = 2.0;
        }
    
        public AccelerateInterpolator(float factor) {
            mFactor = factor;
            mDoubleFactor = 2 * mFactor;
        }
    
        ...
    
        public float getInterpolation(float input) {
            if (mFactor == 1.0f) {
                return input * input;
            } else {
                return (float)Math.pow(input, mDoubleFactor);
            }
        }
    
        ...
    }
    
    

    可见加速度插值器也很简单,默认mFactor为1.0,就对input去平方,随着值增大,结果呈二次递增。另外也可以自定义factor,自定义后就是调用pow方法,求一个幂,也能达到加速效果。

    5.估值器

    TypeEvaluator在我们在学习ValueAnimator的ofObject 方法时已经了解过。估值器和插值器是互相配合的。估值器中的系数fraction就是插值器传来的,估值器负责最后计算出实际的值。这里我们就不详细叙述了。

    6.硬件加速

    使用硬件加速会让动画绘制的更快,因为硬件会把图层缓存在GPU上。
    开启方法:

    view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this,R.animator.anim);
    
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int currentValue = (Integer) animation.getAnimatedValue();
                    view.setRotation(currentValue);
                }
            });
    animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    view.setLayerType(View.LAYER_TYPE_NONE, null);
                }
            });
    animator.start();
    

    基本就是动画开始前开启硬件加速,结束后关闭。

    对于ViewPropertyAnimator,开启方法更加简单,调用withLayer()即可:

    view.animate().alpha(0).withLayer();
    

    7.总结

    可见Android中的动画是非常丰富而且开放的,不仅提供了大量预置的效果,也开放了较底层的接口供我们实现各种丰富的动画效果。

    相关文章

      网友评论

          本文标题:Android动画总结

          本文链接:https://www.haomeiwen.com/subject/pksplftx.html