一、内容概述

本篇所涉及的内容如下

补间动画

- ScaleAnimation 缩放动画
 - AlphaAnimation 透明度动画
 - TranslateAnimation 位移动画
 - RotateAnimation 旋转动画
 - AnimationSet View动画集合
 - 自定义View动画

帧动画 (FrameAnimation 或称 Drawable Animation)

布局动画

- LayoutAnimation 基于View动画实现
 -  LayoutTransition 基于属性动画实现

二、补间动画

原理:通过起点和终点的位置来模拟出中间位置所形成的动画效果
特点:不改变view的自身属性,移动的只是一份影像
优劣:适宜做复杂的动画效果,但不适宜与有用户交互的场合

1.ScaleAnimation 缩放动画

示例

public void scale(View view) {
        Button scaleButton = (Button) view;

//        ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f, 1, 
0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        // 解析xml文件获取ScaleAnimation对象
        ScaleAnimation scaleAnimation = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_scale);
        //  设置动画时长
        scaleAnimation.setDuration(1000);
        // 保持动画后的位置
        scaleAnimation.setFillAfter(true);
        // 设置Interpolator
        scaleAnimation.setInterpolator(new AccelerateInterpolator());
       //设置重复次数
 scaleAnimation.setRepeatCount(Animation.INFINITE);
        // 设置重复模式
   scaleAnimation.setRepeatMode(Animation.REVERSE);
        scaleButton.startAnimation(scaleAnimation);

    }

解析ScaleAnimation

常用构造
- public ScaleAnimation(float fromX, float toX, float fromY, float toY)
- public ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY) //可设置缩放中心
- public ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) //可设置缩放中心(pivotValue)与相对缩放对象(pivotType)
两种相对缩放 (后续补间动画均有此项,不赘述)
pivotType:
    - Animation.RELATIVE_TO_SELF   //相对自己
    - RELATIVE_TO_PARENT    // 相对父容器
中心点 pivotX,pivotY 三种种写法(后续补间动画均有此项,不赘述)
- android:pivotX="50%", 相对自身
    - android:pivotX=="%50p" 相对父容器      
    - android:pivotX="200"   指定位置
常用插值器
@android:anim/overshoot_interpolator
@android:anim/accelerate_decelerate_interpolator
@android:anim/accelerate_interpolator
@android:anim/decelerate_interpolator
@android:anim/anticipate_interpolator
@android:anim/anticipate_overshoot_interpolator
@android:anim/bounce_interpolator
@android:anim/linear_interpolator
@android:anim/cycle_interpolator
常见选项 (后续补间动画均有此项,不赘述)
-  setInterpolator  // 设置插值器
-  setRepeatCount  // 设置重复次数,Animation.INFINITE(-1)表示无限循环
-  setRepeatMode // 设置重复模式 ,Animation.REVERSE反转、Animation.RESTART重新开始
-  setDuration  //设置持续时长(ms)
-  setFillAfter  // 设置是否保持动画后的位置
-  setStartOffset  // 设置开始动画时间延迟
-  setAnimationListener 设置动画监听
-  等。。
补间动画监听
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
            // 动画开始时
            @Override
            public void onAnimationStart(Animation animation) {

            }
            // 动画结束时
            @Override
            public void onAnimationEnd(Animation animation) {

            }
            // 动画结束时
            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
xml文件中的写法(位于anim文件夹)
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="1"
    android:fromYScale="1"
    android:interpolator="@android:anim/overshoot_interpolator"
    android:pivotX="50%"
    android:pivotY="50%p"
    android:toXScale="0"
    android:toYScale="0">
</scale>

2.AlphaAnimation 透明度动画

示例

public void alpha(View view) {
        Button alphaButton = (Button) view;
        //     AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
        // 使用AnimationUtils.loadAnimation加载xml写的动画
        AlphaAnimation alphaAnimation = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_alpha);
//        alphaAnimation.setFillAfter(true);
        alphaAnimation.setDuration(1000);
        alphaAnimation.setRepeatCount(Animation.INFINITE);
        alphaAnimation.setRepeatMode(Animation.REVERSE);
        alphaButton.startAnimation(alphaAnimation);`

    }

常见构造

public AlphaAnimation(float fromAlpha, float toAlpha)
public AlphaAnimation(Context context, AttributeSet attrs)

xml文件中的写法(位于anim文件夹)

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="1"
    android:toAlpha="0"  />

3.TranslateAnimation 位移动画

示例

public void translation(View view) {
        Button translationButton = (Button) view;
//        TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
        TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_translate);
        translateAnimation.setDuration(1000);
        translationButton.startAnimation(translateAnimation);

    }

常见构造

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
        int fromYType, float fromYValue, int toYType, float toYValue)  // 设置平移相对谁平移

xml文件中的写法(位于anim文件夹)

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0"
    android:interpolator="@anim/cycle6"
    android:toXDelta="15"></translate>

4.RotateAnimation 旋转动画

示例

public void rotate(View view) {
        Button rotateButton = (Button) view;
        RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_rotate);
//        RotateAnimation rotateAnimation = new RotateAnimation(0,180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
        rotateAnimation.setDuration(1000);
        rotateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
        rotateAnimation.setRepeatMode(Animation.REVERSE);
//        rotateAnimation.setRepeatCount(Animation.INFINITE);  // -1 无限次
        rotateButton.startAnimation(rotateAnimation);


    }

常见构造

- public RotateAnimation(float fromDegrees, float toDegrees)
- public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)   // 指定旋转中心点
- public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,int pivotYType, float pivotYValue)  // 设置旋转中心点的值与类型

xml文件中的写法(位于anim文件夹)

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:interpolator="@android:anim/overshoot_interpolator"
    android:pivotX="200"
    android:pivotY="200"
    android:repeatCount="5"
    android:toDegrees="360">
</rotate>

5.AnimationSet View动画集合

一个可以容纳四种补间动画的集合动画,通过addAnimation将动画添加至集合动画

示例

public void set(View view) {
        Button setButton = (Button) view;
//        AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.animation_set);
//        set.setRepeatCount(2);
//        set.setRepeatMode(Animation.REVERSE);
//        setButton.startAnimation(set);

        AlphaAnimation aa = new AlphaAnimation(1, 0);
        ScaleAnimation sa = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        RotateAnimation ra = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        AnimationSet set = new AnimationSet(true);
        set.setDuration(1000);
//        set.setStartOffset(1500);  // 开始动画延迟
        set.setRepeatCount(3);
        set.setRepeatMode(Animation.RESTART);
        set.addAnimation(aa);
        set.addAnimation(sa);
        set.addAnimation(ra);
        setButton.startAnimation(set);
    }

xml中写法

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:anim/overshoot_interpolator"
    android:repeatCount="1"
    android:repeatMode="restart"
    android:shareInterpolator="true" >
    <rotate
        android:fromDegrees="0"
        android:interpolator="@android:anim/overshoot_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="360"></rotate>
    <alpha
        android:fromAlpha="0.5"
        android:toAlpha="1"></alpha>
    <scale
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%p"
        android:pivotY="50%"
        android:toXScale="0"
        android:toYScale="0" />
</set>
xml文件中相关参数解析
- android:shareInterpolator 是否共享插值器
- pivotX,pivotY  中心点,参数写法见ScaleAnimation解析

注意事项

- 通过对AnimationSet的使用,可以发现setRepeatCount与setRepeatMode失效
-  动画默认只能一起播放,没有提供按次序执行的方法,提供如下两种参考解决办法
    -  设置一个view动画监听,当前一个动画结束时,执行下一个动画
    -  AnimationSet中设置动画播放延迟,实现次序效果

下面提供第二种解决方案

public void sequence(View view) {
        AnimationSet set = new AnimationSet(false);
        AlphaAnimation aa = new AlphaAnimation(1, 0.5f);
        aa.setDuration(1000);

        ScaleAnimation sa = new ScaleAnimation(1, 0.5f, 1, 0.5f);
        sa.setDuration(1000);
        sa.setStartOffset(2000); // 设置延时

        set.addAnimation(aa);
        set.addAnimation(sa);
        view.startAnimation(set);

    }

6.自定义View动画

自定义View动画,首先要继承Animation类,其次实现initialize(int width, int height, int parentWidth, int parentHeight)和applyTransformation(float interpolatedTime, Transformation t)方法。initialize方法用于做初始化工作,诸如获取被animated对象的宽高,此方法在applyTransformation方法之前调用。applyTransformation方法用于操作对象,进行相关的矩阵变换。

简单示例

实现一个左右晃动的振动效果
package com.yu.customviewanimation;

import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * Created by pecu on 2016/08/25.
 */
public class CustomViewAnimation extends Animation {
    int width,height;
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        this.width=width;
        this.height=height;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        t.getMatrix().setTranslate((float) (Math.sin(interpolatedTime*4*3.14)*width/5),0); // 实现一个左右晃动的振动效果

    }
}


三、帧动画(FrameAnimation)

使用步骤

在drawable目录下创建frame_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/p5" android:duration="300" />
    <item android:drawable="@mipmap/p4" android:duration="300" />
    <item android:drawable="@mipmap/p3" android:duration="300" />
    <item android:drawable="@mipmap/p2" android:duration="300" />
    <item android:drawable="@mipmap/p1" android:duration="300" />

</animation-list>

在java代码中

ImageView imageView = (ImageView) findViewById(R.id.lv_frame);
    AnimationDrawable drawable = (AnimationDrawable) imageView.getDrawable();
    drawable.start();

注意事项

由于帧动画是利用多张图片堆叠出来的效果,加上安卓对每个应用的内存限制,容易造成内存溢出(OOM),因此在开发中慎用帧动画。使用帧动画时,尽量使用尺寸较小的图片

四、布局动画

1、LayoutAnimation

主要用于ViewGroup中的子view的出场动画

使用步骤

在xml中定义LayoutAnimation动画

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/anim_translate"
    android:animationOrder="normal"
    android:delay="0.2"
    android:interpolator="@android:anim/overshoot_interpolator"></layoutAnimation>
关于参数android:delay= fraction,表示item相对于上一个item出场的时间延迟,为0~1之间的float值,即为item出场时间的比例=duration*fraction

具体条目动画anim_translate.xml

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

</translate>
注意:anim_translate中给设置item动画的anim文件设置druation,否则无效果

在xml中给ListView设置布局动画,通过android:layoutAnimation指定

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

亦可java代码中可以直接加载xml中定义的布局动画,然后设置给ListView

listView = (ListView) findViewById(R.id.lv);
    TranslateAnimation ta = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.anim_translate);
    LayoutAnimationController lac = new LayoutAnimationController(ta);
    lac.setDelay(0.5f);
    lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
    listView.setLayoutAnimation(lac);

2、LayoutTransition

用于ViewGroup中的子view的变化对自己,对ViewGroup的影响所需的动画,即有View添加、删除、隐藏、显示的时候才会体现出来。

几种转换动画类型:

1.LayoutTransition.APPEARING:当View出现或者添加的时候View出现的动画。  
2.LayoutTransition.CHANGE_APPEARING:当添加View导致布局容器改变的时候整个布局容器的动画。  
3.LayoutTransition.DISAPPEARING:当View消失或者隐藏的时候View消失的动画。  
4.LayoutTransition.CHANGE_DISAPPEARING:当删除或者隐藏View导致布局容器改变的时候整个布局容器的动画。  
5.LayoutTransition.CHANGE:当不是由于View出现或消失造成对其他View位置造成改变的时候整个布局容器的动画。

使用系统提供的默认动画

只需在ViewGroup中添加一句android:animateLayoutChanges=”true”即可

<GridLayout
        android:id="@+id/gl"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:animateLayoutChanges="true"
        android:columnCount="4"></GridLayout>

使用自定义布局动画

步骤:

1、初始化LayoutTransition对象

LayoutTransition lt = new LayoutTransition();

2、通过setAnimator方法给特定的转换状态指定动画

final ObjectAnimator appearAnim = ObjectAnimator.ofFloat(null, "rotationY", 90, 0);
appearAnim.setDuration(lt.getDuration(LayoutTransition.APPEARING));
lt.setAnimator(LayoutTransition.APPEARING, appearAnim);

3、给ViewGroup设置布局容器动画

gridLayout.setLayoutTransition(lt);
示例
private void setTransition() {
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 1);

        layoutTransition = new LayoutTransition();
        gridLayout.setLayoutTransition(layoutTransition);

        /**
         * 添加一个Button的出现动画
         */
        final ObjectAnimator appearAnim = ObjectAnimator.ofFloat(null, "rotationY", 90, 0);
        appearAnim.setDuration(layoutTransition.getDuration(LayoutTransition.APPEARING));
        layoutTransition.setAnimator(LayoutTransition.APPEARING, appearAnim);

        /**
         * 删除一个Button时的消失动画
         */
        final ObjectAnimator disappearingAnim = ObjectAnimator.ofFloat(null, "scaleX", 1, 0);
        disappearingAnim.setDuration(layoutTransition.getDuration(LayoutTransition.DISAPPEARING));
        layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnim);

        /**
         * LayoutTransition.CHANGE_DISAPPEARING
         * 当删除一个Button时,设置其它Button的动画效果
         * Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。
         */
        Keyframe mKeyframeStart = Keyframe.ofFloat(0.0f, 360f);
        Keyframe mKeyframeMiddle = Keyframe.ofFloat(0.5f, 180f);
        Keyframe mKeyframeEnd = Keyframe.ofFloat(1.0f, 0f);

        PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",
                mKeyframeStart, mKeyframeMiddle, mKeyframeEnd);
        ObjectAnimator changeDisAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight, pvhBottom, mPropertyValuesHolder).setDuration(layoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeDisAppearingAnim);
        changeDisAppearingAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setRotation(0.0f);
            }
        });

        /**
         * LayoutTransition.CHANGE_APPEARING
         * 当增加一个Button时,设置其他Button的动画效果 缩放效果
         */

        PropertyValuesHolder mHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f, 1.0f);
        PropertyValuesHolder mHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f, 1.0f);
        ObjectAnimator changeAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,
                pvhTop, pvhRight, pvhBottom, mHolderScaleX, mHolderScaleY).setDuration(layoutTransition
                .getDuration(LayoutTransition.CHANGE_APPEARING));
        layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAppearingAnim);
        changeAppearingAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1f);
                view.setScaleY(1f);
            }
        });
    }