一、综述


补间动画(Tween Animation、View Animation)是Android的基本动画之一,其与帧动画的本质完全不同。帧动画的原理是逐帧播放Drawable形成动画效果,补间动画是给出关键两帧,在两帧之间补充渐变的过程来实现动画效果。补充渐变来实现动画效果是动画的基本思想,这样看来,帧动画才属于另类,所以除非是完全无规律的动画效果,否则尽量不要使用帧动画。


Android支持的补间动画有四种:平移(Translate)、旋转(Rotate)、缩放(Scale)、透明度(Alpha),这四种效果涵盖了动画效果的基础。应用动画过程通常都不是简单的一个动画效果能搞定的,这时候我们需要使用动画集合(AnimationSet)来将以上四种效果灵活组合起来使用。


补间动画的应用对象是View。



二、xml用法


下文中X表示View布局距左位置,Y表示View布局距上位置,W表示View宽度,H表示View高度



2.1 平移动画


平移动画的本质就是将View的显示位置从某个位置(X0,Y0)移动到(X1,Y1)。注意这里说的显示位置,View在坐标系内的真实位置并未发生变化,所以动画的过程只会触发Draw过程,不会触发Measure过程。




动画开始前在红框位置的View,通过平移100%的动画后效果为绿色区域。



2.1.1 动画定义


动画定义res/anim/文件夹下。下例命名为tween_translate_animation_demo.xml


<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android :fromXDelta="50%"
    android :fromYDelta="50%"
    android :toXDelta="100%"
    android :toYDelta="100%"
    android :fillAfter="true"
    android:interpolator="@android:anim/decelerate_interpolator"
    android :duration="1000">
</translate>


fromXDelta:平移动画的初始帧时X的所在位置;50%表示动画起始于X + 50% * W 的位置


fromYDelta:平移动画的初始帧时Y的所在位置;50%表示动画起始于Y + 50% * H 的位置


toXDelta    :平移动画的结束帧时X的所在位置;100%表示动画结束于X + 100% * W 的位置


toYDelta    :平移动画的结束帧时Y的所在位置;100%表示动画结束于Y + 100% * H 的位置


fillAfter       : true表示动画结束后该动画被应用在界面上,也就是说,界面停留在动画最后一帧(上文的绿色View显示(X + W,Y + H)位置);false表示动画结束后,界面回到初始位置(上文的绿色View显示在X,Y位置)


interpolator: 动画的插值器,用于改变动画渐变过程的速率,常用的有:线性插值(linear_interpolator)、加速插值器(accelerate_interpolator)、减速插值器(decelerate_interpolator)、加减速插值器(accelerate_decelerate_interpolator)等。


duration     : 动画的持续时间



2.1.2 代码集成


首先是从XML中加载动画:


TranslateAnimation mTranslateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.tween_translate_animation_demo );


将动画应用到View上:


mTxtViewFrameAnimationContainer .startAnimation(mTranslateAnimation);


结束动画:


mTxtViewFrameAnimationContainer .clearAnimation();



2.2 缩放动画


与平移动画相同的是,缩放动画也不改变View在布局中的位置,下图展示的缩放前的View(红色框内)与缩放后的View展示位置的对比:




上图是基于View(0,0)位置缩放到自身2倍大小的对比,可以看到View在父元素中并未改变位置,其改变的是draw方法的绘制区域。



2.2.1 动画定义


动画定义res/anim/文件夹下。下例命名为tween_scale_animation_demo.xml


<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android :fromXScale="50%"
    android :fromYScale="50%"
    android :toXScale="200%"
    android :toYScale="200%"
    android :pivotX="10%"
    android :pivotY="10%"
    android :fillAfter="true"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android :duration="1000">
</scale>


fromXScale  : 缩放动画的初始帧中X轴的缩放比例;50%表示动画从50%W开始缩放


fromYScale  : 缩放动画的初始帧中Y轴的缩放比例;50%表示动画从50%H开始缩放


toXScale      : 缩放动画的结束帧中X轴的缩放比例;200%表示View的显示缩放到200%W大小,也就是宽度变大一倍


toYScale      : 缩放动画的结束帧中Y轴的缩放比例;200%表示View的显示缩放到200%H大小,也就是高度变大一倍


pivotX         : 缩放动画的X轴中心点(X轴心),表示动画以此点为中心开始缩放,10%表示以X+10%W为X轴重心


pivotY         : 缩放动画的Y轴中心点(Y轴心),表示动画以此点为中心开始缩放,10%表示以Y+10%H为Y轴重心


其他参数见2.1.1



2.2.2 代码集成


与平移动画完全相同,首先是从XML中加载动画:


ScaleAnimation mScaleAnimation = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.tween_scale_animation_demo );


将动画应用到View上:


mTxtViewFrameAnimationContainer .startAnimation(mScaleAnimation);


结束动画:


mTxtViewFrameAnimationContainer .clearAnimation();



2.3 旋转动画


旋转动画也不改变View在父布局的位置。如下图所示:




红色是View的布局位置,绿色为应用了旋转动画的显示位置。



2.3.1 动画定义


<rotate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android :fromDegrees="180"
    android :toDegrees="-270"
    android :pivotX="10%"
    android :pivotY="10%"
    android :visible="true"
    android :drawable="@mipmap/ic_launcher"
    android :fillAfter="true"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android :duration="1000">
</rotate>


fromDegrees    : 旋转动画的初始帧时View的旋转角度,取值是整数,每360度是旋转一圈,180表示View从顺时针方向以中心点旋转180度作为动画起始位置


toDegrees        : 旋转动画的结束帧时View的旋转角度,取值是整数,-270表示View从逆时针方向以中心点旋转270度作为动画结束位置。fromDegrees > toDegrees则动画逆时针旋转,fromDegrees < toDegrees则动画顺时针旋转


pivotX              : 旋转动画的旋转中心点X坐标(轴点),表示动画以此点为中心旋转,10%表示动画以X+10%W为X中心点旋转


pivotY              : 旋转动画的旋转中心点Y坐标(轴点),表示动画以此点为中心旋转,10%表示动画以Y+10%H为Y中心点旋转


visible              : 设置动画的可见性,在xml中定义无效,true/false动画都照常运行


drawable         : 没找到作用


其他参数见2.1.1



2.3.2 代码集成


集成方式同上,加载动画:


RotateAnimation mRotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(this, R.anim.tween_rotate_animation_demo );


开始动画:


mTxtViewFrameAnimationContainer .startAnimation(mRotateAnimation);


结束动画:


mTxtViewFrameAnimationContainer .clearAnimation();



2.4 透明度动画


透明度动画只改变View显示的透明度,View的展示位置还是和View的布局位置重合的。



2.4.1 动画定义


<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android :fromAlpha="0.2"
    android :toAlpha="1"
    android :fillAfter="true"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android :duration="1000">
</alpha>


fromAlpha  : 透明度动画的初始帧时View的透明度,取值在0~1之间,小于0的等价于0,大于1的等价于1,0.2表示View初始透明度是0.2


toAlpha      : 透明度动画的结束帧时View的透明度,取值在0~1之间,小于0的等价于0,大于1的等价于1,1表示View初始透明度是1,也就是完全不透明


其他参数见2.1.1



2.4.2 代码集成


和上面三种动画完全相同,加载动画:


AlphaAnimation mAlphaAnimation = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.tween_alpha_animation_demo );


开始动画:


mTxtViewFrameAnimationContainer .startAnimation(mAlphaAnimation);


结束动画: 


mTxtViewFrameAnimationContainer .clearAnimation();



2.5 组合动画


一般要实现某种产品动画效果,上面谈到的四种基础动画都不是单独应用的,这时候我们可以通过动画组合将各种动画组装在一起执行,形成组合动画的效果。组合动画对应的Java类是AnimationSet。



2.5.1 动画定义


<set xmlns:android="http://schemas.android.com/apk/res/android"
    android :duration="2000"
    android :fillAfter="true"
    android :fillBefore="true"
    android :repeatMode="reverse"
    android :shareInterpolator="true"
    android:interpolator="@android:anim/accelerate_interpolator"
    android :startOffset="1000">   
    <!--在此处添加translate、rotate 、scale、 alpha动画或者另一个set集合 -->
</set>


duration/fillAfter/interpolator前面都说过了,此处略。


fillBefore               : 标识在动画开始前,是否对View应用动画转化的效果。仅在mFillEnabled为true时有作用,否则其值默认为true


repeatMode          : repeatMode属性有两个取值:reverse和restart,表示动画重复时的执行策略,reverse反转动画到初始帧在继续执行,restart直接从结束帧调到初始帧执行。注意:reverse从结束帧到初始帧的执行也算一次动画执行,repeatCount为4的reverse模式则执行为,开始帧-->结束帧-->开始帧-->结束帧-->开始帧;restart则是开始帧-->结束帧循环4次。


shareInterpolator  : 标识各子动画是否应使用set中定义插值器,在set定义了插值器的情况下,true表示子动画应使用set定义插值器,false表示子动画可以使用自己的插值器。如果set没设置插值器,则此值没有作用


startOffset            : 表示动画集合何时开始执行,1000表示动画集合在1s后开始执行



2.5.2 代码集成


加载动画:


AnimationSet mAnimationSet = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.tween_animation_set_demo );


开始动画:


mTxtViewFrameAnimationContainer .startAnimation(mAnimationSet);


结束动画:


mTxtViewFrameAnimationContainer .clearAnimation();



三、代码实现



3.1 平移动画


mTranslateAnimation = new TranslateAnimation(TranslateAnimation. RELATIVE_TO_SELF, 0.5f ,
        TranslateAnimation.RELATIVE_TO_SELF, 1f,
        TranslateAnimation.RELATIVE_TO_SELF, 0.5f,
        TranslateAnimation.RELATIVE_TO_SELF, 1f);
mTranslateAnimation .setFillAfter(true);
mTranslateAnimation .setInterpolator(new AccelerateDecelerateInterpolator());
mTranslateAnimation .setDuration(1000);
mTranslateAnimation .setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        //动画开始执行时回调
        Log. d("TEST11", "onAnimationStart");
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        //动画结束执行时回调
        Log. d("TEST11", "onAnimationEnd");
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        //动画重复执行时回调
        Log. d("TEST11", "onAnimationRepeat");
    }
});


以上代码与之前的xml定义实现效果完全相同,并监听了动画执行的回调。


TranslateAnimation最常用的构造器是:


public TranslateAnimation( int fromXType, float fromXValue, int toXType, float toXValue,


int fromYType, float fromYValue, int toYType, float toYValue);


public TranslateAnimation( float fromXDelta, float toXDelta, float fromYDelta, float toYDelta);


第一个用的更多些,参数中的type可以取三种值:


TranslateAnimation.RELATIVE_TO_SELF:相对于自己宽高的取值,value的取值是float,值是期望位移值与宽/高的比值


TranslateAnimation.RELATIVE_TO_PARENT:相对于父元素的取值,取值为百分比


TranslateAnimation.ABSOLUTE:绝对值,平移的像素值,传入此参数,如果都传入此参数,则可以缩略为第二个构造器


代码集成与之前相同,见2.1.2,有关TranslateAnimation的其他属性设置后续再讨论。



3.2 缩放动画


mScaleAnimation = new ScaleAnimation(0.5f, 2f, 0.5f, 2f , 0.1f , 0.1f );
mScaleAnimation.setFillAfter(true);
mScaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mScaleAnimation.setDuration(1000 );


ScaleAnimation的构造器有四个:


ScaleAnimation(Context context, AttributeSet attrs):动画从资源中加载,正常情况下用得比较少


ScaleAnimation( float fromX, float toX, float fromY, float toY):给定X轴的开始缩放比例结束缩放比例,Y轴开始缩放比例到Y轴缩放比例,构建一个缩放动画;fromX是动画开始时的水平缩放比例,toX是动画结束时的水平缩放比例,fromY是动画开始时的垂直缩放比例,toY是动画结束时的垂直缩放比例,比例值是相对于View宽高的,所以动画效果展示View的可视宽度从fromX * W缩放到toX*W,View可视高度从fromY * H缩放到toY * W; 

 

  ScaleAnimation( float fromX, float toX, float fromY, float toY, float pivotX, float pivotY):此接口与上一个接口的区别就在于指定了缩放中心点,缩放中心点前文已叙,此处不再展开。 

 

  ScaleAnimation( float fromX, float toX, float fromY, float toY, 

 

    int pivotXType, float pivotXValue, int pivotYType, float pivotYValue): 

 

  pivotXType、pivotYType取值可以包括三种: 

 

  Animation.ABSOLUTE:表示后面的value是绝对位置,也就是说,如果两个type取值都是绝对值,则View按View坐标系的(pivotXValue, pivotYValue)为中心点缩放。pivotXValue=10,pivotYValue=10意味着View缩放中心点是(X+10, Y+10) 

 

  Animation.RELATIVE_TO_SELF:表示后面的value是相对位置,相对的是自己的宽高,取值是百分比,具体见2.2.1 

 

  Animation.RELATIVE_TO_PARENT:表示后面的value是相对父元素的宽高做中心点的,取值也是百分比


代码集成与之前相同,见2.2.2



3.3 旋转动画


mRotateAnimation = new RotateAnimation(-180, -90,
        Animation.RELATIVE_TO_SELF, 0.1f,
        Animation.RELATIVE_TO_SELF, 0.1f);
mRotateAnimation.setFillAfter(true);
mRotateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mRotateAnimation.setDuration(1000 );


RotateAnimation的构造方法:

RotateAnimation(Context context, AttributeSet attrs):从XML资源中解析动画 

 

  RotateAnimation( float fromDegrees, float toDegrees):取值解释见2.3.1 

 

  RotateAnimation( float fromDegrees, float toDegrees, float pivotX, float pivotY):中心点取值解释见3.2部分 

 

  RotateAnimation( float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue):中心点类型和取值见3.2,其他接口见下一篇文章。代码集成见2.3.2


3.4 透明度动画


mAlphaAnimation = new AlphaAnimation(0.2f, 1f);
mAlphaAnimation.setFillAfter(true);
mAlphaAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mAlphaAnimation.setDuration(1000 );


AlphaAnimation的构造方法最简单,因为它没有中心点的概念:


AlphaAnimation(Context context, AttributeSet attrs):基于资源构造动画


AlphaAnimation( float fromAlpha, float toAlpha):fromAlpha是动画开始时的View的透明度,toAlpha是动画结束时View的透明度


代码集成见2.4.2



3.5 组合动画


mAnimationSet = new AnimationSet(false);
mAnimationSet.setDuration(2000 );
mAnimationSet.setFillAfter(true);
mAnimationSet.setFillBefore(false);
mAnimationSet.setRepeatMode(Animation.REVERSE);
mAnimationSet.setInterpolator(new AccelerateInterpolator());
mAnimationSet.setStartOffset(1000 );

AlphaAnimation alphaAnimation = new AlphaAnimation(0.2f, 1f);
alphaAnimation.setFillAfter(true );
alphaAnimation.setInterpolator(new AccelerateInterpolator());
alphaAnimation.setDuration(1000 );
mAnimationSet.addAnimation(alphaAnimation);

RotateAnimation rotateAnimation = new RotateAnimation(-180, -90,
        Animation.RELATIVE_TO_SELF, 0.1f,
        Animation.RELATIVE_TO_SELF, 0.1f);
rotateAnimation.setFillAfter(true );
rotateAnimation.setDuration(1000 );
mAnimationSet.addAnimation(rotateAnimation);
...


AnimationSet构造方法:


AnimationSet(Context context, AttributeSet attrs):通过资源定义构造对象


AnimationSet( boolean shareInterpolator):传入shareInterpolator标识,true标识所有的子动画都需要使用AnimationSet中定义的插值器,false标识各动画使用各自定义的插值器


其他接口作用见2.5.1,代码集成见2.5.2



四、源码分析


源码分析比较多,后面专门讨论。



五、兼容性


部分机型不兼容Alpha通道,导致透明度动画不执行