参考文献:

Android 动画总结
Android 一共有多少种动画?准确告诉你!

1. 帧动画

参考文献:Drawable Animation (Drawable动画)使用详解

帧动画是最容易实现的一种动画,这种动画更多的依赖于完善的 UI 资源,他的原理就是将一张张单独的图片连贯的进行播放,从而在视觉上产生一种动画的效果;有点类似于某些软件制作 gif 动画的方式。

drawable/anim.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
    <item android:drawable="@drawable/ic_01" android:duration="100" />
    <item android:drawable="@drawable/ic_02" android:duration="100" />
    <item android:drawable="@drawable/ic_03" android:duration="100" />
</animation-list>
  • android:oneshot="false" 动画循环执行
  • android:oneshot="true" 动画执行一次

使用方式

<ImageView
    android:id="@+id/frame_anim"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:src="@drawable/anim" />
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_frame_animation);
    ImageView animationImg =  findViewById(R.id.frame_anim);
    AnimationDrawable animDraw = (AnimationDrawable) animationImg.getDrawable();
    // 动画开始
    animDraw.start();
    // 动画停止
    animDraw.stop();
}

特别注意,AnimationDrawable 的 start() 方法不能在 Activity 的 onCreate 方法中调运,因为 AnimationDrawable 还未完全附着到 window 上, 所以最好的调运时机是 onWindowFocusChanged() 方法中。

2. 补间动画

参考文献:View Animation(视图动画)使用详解

补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)
补间动画的实现,一般会采用 xml 文件的形式;代码会更容易书写和阅读,同时也更容易复用。

特别特别注意:
补间动画执行之后并未改变 View 的真实布局属性值。切记这一点,譬如我们在 Activity 中有一个 Button 在屏幕上方, 我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方,这时如果点击屏幕下方动画执行之后的 Button 是没有任何反应的, 而点击原来屏幕上方没有 Button 的地方却响应的是点击 Button 的事件。

2.1 XML 实现

首先,在 res/anim/ 文件夹下定义如下的动画实现方式

alpha_anim.xml 动画实现

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:toAlpha="0.0" />

scale.xml 动画实现

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromXScale="0.0"
    android:fromYScale="0.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="1.0"
    android:toYScale="1.0"/>

translate.xml 动画实现

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_longAnimTime"
    android:fromXDelta="100%"
    android:toXDelta="0" />

使用动画

Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_anim);
img = (ImageView) findViewById(R.id.img);
img.startAnimation(animation);

组合动画

set 可用于将多个动画组合到一起,set 可嵌套

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    
    <set>
        ...
    </set>
</set>

2.2 Java 代码实现

有时候,动画的属性值可能需要动态的调整,这个时候使用 xml 就不合适了,需要使用 java 代码实现

private void RotateAnimation() {
    animation = new RotateAnimation(-deValue, deValue, Animation.RELATIVE_TO_SELF,
            pxValue, Animation.RELATIVE_TO_SELF, pyValue);
    animation.setDuration(timeValue);

    if (keep.isChecked()) {
        animation.setFillAfter(true);
    } else {
        animation.setFillAfter(false);
    }
    if (loop.isChecked()) {
        animation.setRepeatCount(-1);
    } else {
        animation.setRepeatCount(0);
    }

    if (reverse.isChecked()) {
        animation.setRepeatMode(Animation.REVERSE);
    } else {
        animation.setRepeatMode(Animation.RESTART);
    }
    img.startAnimation(animation);
}
  • animation.setFillAfter 决定了动画在播放结束时是否保持最终的状态;
  • animation.setRepeatCount 决定了动画的重复次数
  • animation.setRepeatMode 动画的重复方式

2.3 属性详解

2.3.1 类继承关系

视图动画相关的类继承关系:

java 类名xml 关键字描述信息
AlphaAnimation`` 放置在 res/anim/ 目录下渐变透明度动画效果
RotateAnimation`` 放置在 res/anim/ 目录下画面转移旋转动画效果
ScaleAnimation`` 放置在 res/anim/ 目录下渐变尺寸伸缩动画效果
TranslateAnimation`` 放置在 res/anim/ 目录下画面转换位置移动动画效果
AnimationSet`` 放置在 res/anim/ 目录下一个持有其它动画元素 alpha、scale、translate、rotate 或者其它 set 元素的容器

2.3.2 Animation 属性详解

xml 属性java 方法解释
android:detachWallpapersetDetachWallpaper(boolean)是否在壁纸上运行
android:durationsetDuration(long)动画持续时间,毫秒为单位
android:fillAftersetFillAfter(boolean)控件动画结束时是否保持动画最后的状态
android:fillBeforesetFillBefore(boolean)控件动画结束时是否还原到开始动画前的状态
android:fillEnabledsetFillEnabled(boolean)与android:fillBefore效果相同
android:interpolatorsetInterpolator(Interpolator)设定插值器(指定的动画效果,譬如回弹等)
android:repeatCountsetRepeatCount(int)重复次数
android:repeatModesetRepeatMode(int)重复类型有两个值,reverse表示倒序回放,restart表示从头播放
android:startOffsetsetStartOffset(long)调用start函数之后等待开始运行的时间,单位为毫秒
android:zAdjustmentsetZAdjustment(int)表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal

2.3.3 Alpha 属性详解

xml 属性java方法解释
android:fromAlphaAlphaAnimation(float fromAlpha, …)动画开始的透明度(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlphaAlphaAnimation(…, float toAlpha)动画结束的透明度,同上

2.3.4 Rotate 属性详解

xml 属性java方法解释
android:fromDegreesRotateAnimation(float fromDegrees, …)旋转开始角度,正代表顺时针度数,负代表逆时针度数
android:toDegreesRotateAnimation(…, float toDegrees, …)旋转结束角度,正代表顺时针度数,负代表逆时针度数
android:pivotXRotateAnimation(…, float pivotX, …)缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:pivotYRotateAnimation(…, float pivotY)缩放起点Y坐标,同上规律

2.3.5 Scale 属性详解

xml 属性java方法解释
android:fromXScaleScaleAnimation(float fromX, …)初始X轴缩放比例,1.0表示无变化
android:toXScaleScaleAnimation(…, float toX, …)结束X轴缩放比例
android:fromYScaleScaleAnimation(…, float fromY, …)初始Y轴缩放比例
android:toYScaleScaleAnimation(…, float toY, …)结束Y轴缩放比例
android:pivotXScaleAnimation(…, float pivotX, …)缩放起点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:pivotYScaleAnimation(…, float pivotY)缩放起点Y轴坐标,同上规律

2.3.6 Translate 属性详解

xml 属性java方法解释
android:fromXDeltaTranslateAnimation(float fromXDelta, …)起始点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:fromYDeltaTranslateAnimation(…, float fromYDelta, …)起始点Y轴从标,同上规律
android:toXDeltaTranslateAnimation(…, float toXDelta, …)结束点X轴坐标,同上规律
android:toYDeltaTranslateAnimation(…, float toYDelta)结束点Y轴坐标,同上规律

2.3.7 AnimationSet 详解

AnimationSet 继承自 Animation,是上面四种的组合容器管理类,没有自己特有的属性,他的属性继承自 Animation,所以特别注意, 当我们对 set 标签使用 Animation 的属性时会对该标签下的所有子控件都产生影响。

2.4 动画调用

使用:

ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

上面就是一个标准的使用我们定义的补间动画的模板。至于补间动画的使用,Animation 还有如下一些比较实用的方法介绍:

Animation 类的方法解释
reset()重置 Animation 的初始化
cancel()取消 Animation 动画
start()开始 Animation 动画
setAnimationListener(AnimationListener listener)给当前 Animation 设置动画监听
hasStarted()判断当前 Animation 是否开始
hasEnded()判断当前 Animation 是否结束

既然补间动画只能给 View 使用,那就来看看 View 中和动画相关的几个常用方法吧,如下:

View类的常用动画操作方法解释
startAnimation(Animation animation)对当前 View 开始设置的 Animation 动画
clearAnimation()取消当 View 在执行的 Animation 动画

2.5 Interpolator 插值器

2.5.1 系统插值器

其实各种插值器都是实现了 Interpolator 接口而已,同时可以看见系统提供了许多已经实现 OK 的插值器,
具体如下:

java 类xml id值描述
AccelerateDecelerateInterpolator@android:anim/accelerate_decelerate_interpolator动画始末速率较慢,中间加速
AccelerateInterpolator@android:anim/accelerate_interpolator动画开始速率较慢,之后慢慢加速
AnticipateInterpolator@android:anim/anticipate_interpolator开始的时候从后向前甩
AnticipateOvershootInterpolator@android:anim/anticipate_overshoot_interpolator类似上面 AnticipateInterpolator
BounceInterpolator@android:anim/bounce_interpolator动画结束时弹起
CycleInterpolator@android:anim/cycle_interpolator循环播放速率改变为正弦曲线
DecelerateInterpolator@android:anim/decelerate_interpolator动画开始快然后慢
LinearInterpolator@android:anim/linear_interpolator动画匀速改变
OvershootInterpolator@android:anim/overshoot_interpolator向前弹出一定值之后回到原来位置
PathInterpolator 新增,定义路径坐标后按照路径坐标来跑。

2.5.2 自定义插值器

有时候你会发现系统提供的插值器不够用,可能就像 View 一样需要自定义。所以接下来我们来看看插值器的自定义, 关于插值器的自定义分为两种实现方式,xml 自定义实现(其实就是对现有的插值器的一些属性修改)或者 java 代码实现方式。如下我们来说说。

先看看 XML 自定义插值器的步骤:

  1. 在 res/anim/ 目录下创建 filename.xml 文件。

  2. 修改你准备自定义的插值器如下:

    <?xml version="1.0" encoding="utf-8"?>
     <InterpolatorName xmlns:android="http://schemas.android.com/apk/res/android"
         android:attribute_name="value" />
    
  3. 在你的补间动画文件中引用该文件即可。

可以看见上面第二步修改的是现有插值器的一些属性,但是有些插值器却不具备修改属性,具体如下:

  • <accelerateDecelerateInterpolator>

    • 无可自定义的 attribute。
  • <accelerateInterpolator>

    • android:factor 浮点值,加速速率(默认值为1)。
  • <anticipateInterploator>

    • android:tension 浮点值,起始点后拉的张力数(默认值为2)。
  • <anticipateOvershootInterpolator>

    • android:tension 浮点值,起始点后拉的张力数(默认值为2)。
    • android:extraTension 浮点值,拉力的倍数(默认值为1.5)。
  • <bounceInterpolator>

    • 无可自定义的 attribute。
  • <cycleInterplolator>

    • android:cycles 整形,循环的个数(默认为1)。
  • <decelerateInterpolator>

    • android:factor 浮点值,减速的速率(默认为1)。
  • <linearInterpolator>

    • 无可自定义的 attribute。
  • <overshootInterpolator>

    • android:tension 浮点值,超出终点后的张力(默认为2)。

再来看看 Java 自定义插值器:

Java 自定义插值器其实是 xml 自定义的升级,也就是说如果我们修改 xml 的属性还不能满足需求,那就可以选择通过 Java 来实现方式。

可以看见上面所有的 Interpolator 都实现了 Interpolator 接口,而 Interpolator 接口又继承自 TimeInterpolator, TimeInterpolator 接口定义了一个 float getInterpolation(float input);方法,这个方法是由系统调用的, 其中的参数 input 代表动画的时间,在 0 和 1 之间,也就是开始和结束之间。

如下就是一个动画始末速率较慢、中间加速的 AccelerateDecelerateInterpolator 插值器:

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    ......
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
    ......
}

到此整个补间动画与补间动画的插值器都分析完毕了。