笔记 for:各种安卓动画; 集合 for:好看动画收集和仿写;库 for:动画便利库
Java
Permalink
Failed to load latest commit information.
.gradle/2.14.1
.idea
app
ayo-animate
build/generated
gradle/wrapper
img
.gitignore
LICENSE
README.md
ayo-anim.aar
build.gradle
config.properties
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle
v-m-theme.md

README.md

AyoAnim

笔记 for:各种安卓动画; 集合 for:好看动画收集和仿写;库 for:动画便利库

apk下载:

没整到jcenter上,暂时就用aar包吧,也挺省劲:下载地址
下载下来文件名是:ayo-anim.aar,拷到libs目录里
然后在build.gradle里引入:

repositories {
    flatDir {
        dirs 'libs'
    }
}

compile(name:'ayo-anim', ext:'aar')
  • 特别说明:

    • 本项目既是动画的学习笔记和demo
    • 也是收集了一些好看的好用的好玩的动画效果
    • 最主要的,就是一个通用动画库,本来之前一直用daimajia的ease和AndroidViewAnimation,但发现hujiaweibujidao的yava和wava代码更简洁
    • hujiawei ease:https://github.com/hujiaweibujidao/yava
    • hujiawei anim:https://github.com/hujiaweibujidao/wava
    • 代码家 ease:https://github.com/daimajia/AnimationEasingFunctions
    • 代码家 anim:https://github.com/daimajia/AndroidViewAnimations
    • 注意:hujiawei的代码是代码家的衍生,是改进,而不是再造的轮子
    • 像Flyco提供了一组控件库,挺好用,但动画特效确实基于他们自己的一个库,太不统一了,这种情况以后要避免,谁用谁自己避免
    • hujiawei这个怎么都好,就是名字不讲究,其实给开源项目起名字,是很重要的
    • 另外注意:nineold都将被剔除,不再考虑
  • 关于animator库

    • 在ayo-animate里
    • ease包下是hujiawei的ease库,只不过我改了一下
    • 为什么改,为了和YoYo结构一样,一组子类代表效果,一个枚举整合

1 动画基本概念

这里我们从ValueAnimator出发,来说说TypeEvaluator和TimeInterpolator

首先要明白,动画就是组件的某个属性从一个值到另一个值的变化过程,在变化的持续时间内,属性值产生一系列的连续变化,
将这些属性值作用于组件上,就看到了动画效果

ValueAnimator决定了:每一帧在哪里取,取多少帧
TimeInterpolator:插值器的输入是时间变化率,其实就是时间,输出的是根据时间变化率得到的属性值变化率
所以如果使用LinearInterpolator,时间变化率和属性值变化率就是线性关系,相当于跳过了Interpolator计算过程
TypeEvaluator:估值器是根据属性值的变化率计算真正的属性值,这取决于属性值的类型,int,float,color等
默认的IntEvaluator,FloatEvaluator,是最典型的行为,我们也可以认为这种行为相当于跳过了Evaluate过程

1 TimeInterpolator和TypeEvaluator

先来说说插值器的基本用法,注意,下面代码中的两个动画效果是一样的

ValueAnimator animator1 = new ValueAnimator();
animator1.setFloatValues(0.0f, 1.0f);
animator1.setDuration(1000);
animator1.setInterpolator(new LinearInterpolator());//传入null也是LinearInterpolator
animator1.setEvaluator(new TypeEvaluator() {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        return 100 * fraction;
    }
});
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.e("demo 1", "" + animation.getAnimatedValue());
    }
});

ValueAnimator animator2 = new ValueAnimator();
animator2.setFloatValues(0.0f, 1.0f);
animator2.setDuration(1000);
animator2.setInterpolator(new Interpolator() {
    @Override
    public float getInterpolation(float input) {
        return 100 * input;
    }
});
animator2.setEvaluator(new TypeEvaluator() {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        return fraction;
    }
});
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.e("demo 2", "" + animation.getAnimatedValue());
    }
});

animator1.start();
animator2.start();

安卓内置的Evaluator

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

还有个IntEvaluator IntArrayEvalulator
还有个FloatArrayEvaluator

int,float的Evaluator还好理解,还有个ObjectEvaluator,对象的值怎么过渡呢,例如Point的过渡
理解了这个有助于后面理解Path动画,因为动画所动的是在Path上的下一帧位置

public class Point {  

    private float x;  

    private float y;  

    public Point(float x, float y) {  
        this.x = x;  
        this.y = y;  
    }  

    public float getX() {  
        return x;  
    }  

    public float getY() {  
        return y;  
    }  

}  

public class PointEvaluator implements TypeEvaluator{  

    @Override  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        Point startPoint = (Point) startValue;  
        Point endPoint = (Point) endValue;  
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());  
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());  
        Point point = new Point(x, y);  
        return point;  
    }  

}  

//应用Point:这个只是平移
//如果两点之间是一段曲线,道理也一样,不过需要其他套路了
Point point1 = new Point(0, 0);  
Point point2 = new Point(300, 300);  
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);  
anim.setDuration(5000);  
anim.start();  

Color,也就是Argb的估值器:

public class ColorEvaluator implements TypeEvaluator {  

    private int mCurrentRed = -1;  

    private int mCurrentGreen = -1;  

    private int mCurrentBlue = -1;  

    @Override  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        String startColor = (String) startValue;  
        String endColor = (String) endValue;  
        int startRed = Integer.parseInt(startColor.substring(1, 3), 16);  
        int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);  
        int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);  
        int endRed = Integer.parseInt(endColor.substring(1, 3), 16);  
        int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);  
        int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);  
        // 初始化颜色的值  
        if (mCurrentRed == -1) {  
            mCurrentRed = startRed;  
        }  
        if (mCurrentGreen == -1) {  
            mCurrentGreen = startGreen;  
        }  
        if (mCurrentBlue == -1) {  
            mCurrentBlue = startBlue;  
        }  
        // 计算初始颜色和结束颜色之间的差值  
        int redDiff = Math.abs(startRed - endRed);  
        int greenDiff = Math.abs(startGreen - endGreen);  
        int blueDiff = Math.abs(startBlue - endBlue);  
        int colorDiff = redDiff + greenDiff + blueDiff;  
        if (mCurrentRed != endRed) {  
            mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,  
                    fraction);  
        } else if (mCurrentGreen != endGreen) {  
            mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,  
                    redDiff, fraction);  
        } else if (mCurrentBlue != endBlue) {  
            mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,  
                    redDiff + greenDiff, fraction);  
        }  
        // 将计算出的当前颜色的值组装返回  
        String currentColor = "#" + getHexString(mCurrentRed)  
                + getHexString(mCurrentGreen) + getHexString(mCurrentBlue);  
        return currentColor;  
    }  

    /** 
     * 根据fraction值来计算当前的颜色。 
     */  
    private int getCurrentColor(int startColor, int endColor, int colorDiff,  
            int offset, float fraction) {  
        int currentColor;  
        if (startColor > endColor) {  
            currentColor = (int) (startColor - (fraction * colorDiff - offset));  
            if (currentColor < endColor) {  
                currentColor = endColor;  
            }  
        } else {  
            currentColor = (int) (startColor + (fraction * colorDiff - offset));  
            if (currentColor > endColor) {  
                currentColor = endColor;  
            }  
        }  
        return currentColor;  
    }  

    /** 
     * 将10进制颜色值转换成16进制。 
     */  
    private String getHexString(int value) {  
        String hexString = Integer.toHexString(value);  
        if (hexString.length() == 1) {  
            hexString = "0" + hexString;  
        }  
        return hexString;  
    }  

}  

///使用
ObjectAnimator anim = ObjectAnimator.ofObject(myAnimView, "color", new ColorEvaluator(),   
    "#0000FF", "#FF0000");  
anim.setDuration(5000);  
anim.start();  
  • 总结:
    • 插值器根据时间流逝,计算出动画流逝fraction,例如duration是10秒,时间过了5秒,但fraction是0.2,表示前五秒是慢速运行的,而后5秒,需要走完0.8,所以是加速的
      • 所以插值器管的应该是速度,但fraction也可以是负值,即在初始值之前,或者结束值之后,以此可以产生晃动效果
      • 你应该问插值器,时间已过5秒,动画的进度是多少了?
    • 估值器根据动画流逝,计算出这一帧的属性值,属性可能是int,float这种值类型,也可能是Color,也可能Point和Path的关系计算
      • 所以估值器管的是这一帧的属性值,你应该问估值器,动画进度已经过了0.2了(fraction),这时的属性值应该是多少
    • 所以如果是Linear的插值器,时间流逝即动画进度,则所有处理进度的逻辑,和计算属性值的逻辑,都可以交给估值器来算

基于上面的示例,其实daimajia的开源项目AnimationEasingFunctions,也是基于Evaluator的
而hujiawei的ease库,同时支持插值用法和估值用法

2 ease库用法

ease库是取自: https://github.com/hujiaweibujidao/yava

///枚举用法:as Evaluator
ObjectAnimator animator1 = new ObjectAnimator();
animator1.setTarget(textView1);
animator1.setPropertyName("translationY");
animator1.setFloatValues(0f, -100f);
animator1.setDuration(1000);

animator1.setInterpolator(new LinearInterpolator());
animator1.setEvaluator(EasingFunction.BOUNCE_OUT.getEasingFunction()); //use `EasingFunction.BOUNCE_OUT` as `TypeEvaluator`
animator1.start();


///枚举用法:as Interpolator
ObjectAnimator animator2 = new ObjectAnimator();
animator2.setTarget(textView2);
animator2.setPropertyName("translationY");
animator2.setFloatValues(0f, -100f);
animator2.setDuration(1000);

animator2.setInterpolator(EasingFunction.BOUNCE_OUT.getEasingFunction()); //use `EasingFunction.BOUNCE_OUT` as `Interpolator`
animator2.setEvaluator(new FloatEvaluator());

animator2.start();

///自定义ease函数(也支持插值用法和估值用法)
ObjectAnimator animator1 = new ObjectAnimator();
animator1.setTarget(textView1);
animator1.setPropertyName("translationY");
animator1.setFloatValues(0f, -100f);
animator1.setDuration(1000);

animator1.setInterpolator(new LinearInterpolator());
animator1.setEvaluator(EasingFunction.with(new IFunction() { //customized TypeEvaluator
    @Override
    public float getValue(float input) {
        return input * 2 + 3;
    }
}));

animator1.start();

///如何支持ColorEvaluator或者ARGBEvaluator

安卓内置的几个插值器:

TimeInterpolator   --> Interpolator --> BaseInterpolator

LinearInterpolator
AccelerateInterpolator:开始慢,后面快
DecelerateInterpolator
AccelerateDecelerateInterpolator:开始慢,结束慢,中间快
AnticipateInterpolator
OvershootInterpolator
AnticipateOvershootInterpolator
BounceInterpolator
CycleInterpolator

PathInterpolator

安卓内置的几个估值器:

TypeEvaluator

ArgbEvaluator
FloatEvaluator
FloatArrayEvaluator
IntEvaluator
IntArrayEvaluator
PointFEvaluator
RectEvaluator

3 YoYo库用法:属性动画

YoYoString rope =  YoYo.with(Techniques.FadeIn).duration(duration)
                .interpolate(Functions.Accelerate)
                .listen(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationCancel(Animator animation) {
                        Toast.makeText(DemoYoyoActivity.this, "取消了", Toast.LENGTH_SHORT).show();
                    }
                })
                .playOn(mTarget);

//停止动画
rope.stop(); 

4 帧动画

多个图片配合形成动画,类似gif图,众所周知比较耗费资源,但有时也是最有效的

这里只说说用法

//在res/drawable,创建anim1.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- 
    根标签为animation-list,其中oneshot代表着是否只展示一遍,设置为false会不停的循环播放动画
    根标签下,通过item标签对动画中的每一个图片进行声明
    android:duration 表示展示所用的该图片的时间长度
 -->
<animation-list
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:oneshot="true"
  >
    <item android:drawable="@drawable/icon1" android:duration="150"></item>
    <item android:drawable="@drawable/icon2" android:duration="150"></item>
    <item android:drawable="@drawable/icon3" android:duration="150"></item>
    <item android:drawable="@drawable/icon4" android:duration="150"></item>
    <item android:drawable="@drawable/icon5" android:duration="150"></item>
    <item android:drawable="@drawable/icon6" android:duration="150"></item>
</animation-list>

将其作为ImageView的src属性,或者其他控件的background属性
<ImageView android:id="@+id/animationIV"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="5px"
    android:src="@drawable/animation1"/>

Java代码
iv.setImageResource(R.drawable.animation1);
animationDrawable = (AnimationDrawable) animationIV.getDrawable();
animationDrawable.start();

5 补间动画

  • View动画:
    • 平移:TranslateAnimation
    • 透明度:AlphaAnimation
    • 旋转:RotateAnimation
    • 缩放:ScaleAnimation
    • 无论如何移动原View,View始终占据原先的位置,在新位置点击也不会触发点击事件

5.1 入门

在Java代码中创建动画

///-----------------平移
TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
            int fromYType, float fromYValue, int toYType, float toYValue)
fromXType    :在X轴上动画相对于谁移动,一般是相对于父控件Animation.RELATIVE_TO_PARENT,或者相对于自己移动Animation.RELATIVE_TO_SELF
fromXValue    :在X轴上的起始时的移动距离,一般为0f,即原来位置
toXType        :在结束时X轴相对于谁移动,同fromXType
toXValue    :在结束时动画X轴所处的距离,若相对于父控件移动,一般不超多1.0f,否则会移动到屏幕外面
fromYType    :在Y轴上动画相对于谁移动,一般是相对于父控件Animation.RELATIVE_TO_PARENT,或者相对于自己移动Animation.RELATIVE_TO_SELF
fromYValue    :在Y轴上的起始时的移动距离,一般为0f,即原来位置
toYType        :在结束时Y轴相对于谁移动,同fromXType
toYValue    :在结束时动画Y轴所处的距离,若相对于父控件移动,一般不超多1.0f,否则会移动到屏幕外面

//相对于父控件,往右下角45度移动的示例
TranslateAnimation trans = new TranslateAnimation(
        Animation.RELATIVE_TO_PARENT, 0f,    // 起始时X轴相对于父控件移动
        Animation.RELATIVE_TO_PARENT, 0.2f,     // 结束时X轴也相对于父控件移动
        Animation.RELATIVE_TO_PARENT, 0,    // 起始时Y轴相对于父控件移动
        Animation.RELATIVE_TO_PARENT, 0.2f);    // 结束时Y轴相对于父控件移动
trans.setDuration(2000);
imageView.startAnimation(trans);

///-----------------透明度
//1.0意味着着完全不透明 0.0意味着完全透明
AlphaAnimation aa = new AlphaAnimation(1.0f, 0.0f);
aa.setDuration(2000); //设置动画执行的时间
aa.setRepeatCount(1); //设置重复的次数
aa.setRepeatMode(Animation.REVERSE);//设置动画执行的模式
//imageView开始执行动画 
imageView.startAnimation(aa);

///-----------------旋转
RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
        int pivotYType, float pivotYValue)
fromDegrees : 从哪个角度开始旋转,一般为0
toDegrees    : 结束的时候角度为多少,如转一圈则为360
pivotXType    : 在X轴上动画相对于谁旋转,一般是相对于父控件Animation.RELATIVE_TO_PARENT,或者相对于自己旋转Animation.RELATIVE_TO_SELF
pivotXValue    : 在X轴上动画旋转的中心点
pivotYType    : 在Y轴上动画相对于谁旋转,同pivotXType
pivotYValue    : 在Y轴上动画旋转的中心点

//以imageView的中心点旋转360度回到原点
RotateAnimation rotate = new RotateAnimation(0, 360, // 从0度开始旋转,到360度结束
        Animation.RELATIVE_TO_SELF, 0.5f,     // X轴指定相对于自己还是相对于父控件旋转,0.5f则是以自己宽度的二分之一为旋转点
        Animation.RELATIVE_TO_SELF, 0.5f);    // Y轴指定相对于自己还是相对于父控件旋转,0.5f则是以自己高度的二分之一为旋转点
rotate.setDuration(2000);
imageView.startAnimation(rotate);

///-----------------缩放
ScaleAnimation(float fromX, float toX, float fromY, float toY,
            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
fromX    : 动画的X轴从多大开始缩放,一般为1.0f,即动画原本大小开始缩放
toX        : 动画在X轴方向上要缩放到多大,如果小于1.0f,则动画在X轴上的距离会变小,大于1.0f会放大
fromY    : 动画的Y轴从多大开始缩放,其他同fromX
toY        : 动画在Y轴方向上要缩放到多大,其他同toX
pivotXType    : X轴指定相对于自己还是相对于父控件缩放,一般是相对于父控件Animation.RELATIVE_TO_PARENT,或者相对于自己缩放Animation.RELATIVE_TO_SELF
pivotXValue    : 指定X轴方向上哪个位置为中心缩放
pivotYType    : Y轴同pivotXType
pivotYValue    : Y轴同pivotXValue

//相对于自己从中心点放大
ScaleAnimation scale = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f,     // 1.0f代表原本大小,2.0f代表放大一倍
        Animation.RELATIVE_TO_SELF, 0.5f, // X轴指定相对于自己还是相对于父控件放大,第二个则是以哪个位置为X轴的旋转点,此处为中心点放大
        Animation.RELATIVE_TO_SELF, 0.5f);
scale.setDuration(2000);
imageView.startAnimation(scale);

在xml中创建动画

//Java代码加载xml动画
Animation translate = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.translate);
//开始动画
iv.startAnimation(translate);

//在anim目录下

<?xml version="1.0" encoding="utf-8"?>
<translate
    android:fromXDelta="0%p"    // 起始位置为原来的位置,后面带P代表相对于父控件移动,即代码布局中的Animation.RELATIVE_TO_PARENT
    android:toXDelta="0%p"
    android:fromYDelta="0%p"
    android:toYDelta="20%p"            
    android:fillAfter="true"
    android:duration="2000"
    xmlns:android="http://schemas.android.com/apk/res/android">

</translate>

动画集合

5.2 自定义补间动画

主要是Matrix和Camera的使用,所以有点麻烦,下面以ApiDemo里的Rotate3D代码为例


import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.graphics.Camera;
import android.graphics.Matrix;

/**
 * An animation that rotates the view on the Y axis between two specified angles.
 * This animation also adds a translation on the Z axis (depth) to improve the effect.
 */
public class Rotate3dAnimation extends Animation {
    private final float mFromDegrees;
    private final float mToDegrees;
    private final float mCenterX;
    private final float mCenterY;
    private final float mDepthZ;
    private final boolean mReverse;
    private Camera mCamera;

    /**
     * Creates a new 3D rotation on the Y axis. The rotation is defined by its
     * start angle and its end angle. Both angles are in degrees. The rotation
     * is performed around a center point on the 2D space, definied by a pair
     * of X and Y coordinates, called centerX and centerY. When the animation
     * starts, a translation on the Z axis (depth) is performed. The length
     * of the translation can be specified, as well as whether the translation
     * should be reversed in time.
     *
     * @param fromDegrees the start angle of the 3D rotation
     * @param toDegrees the end angle of the 3D rotation
     * @param centerX the X center of the 3D rotation
     * @param centerY the Y center of the 3D rotation
     * @param reverse true if the translation should be reversed, false otherwise
     */
    public Rotate3dAnimation(float fromDegrees, float toDegrees,
            float centerX, float centerY, float depthZ, boolean reverse) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mCenterX = centerX;
        mCenterY = centerY;
        mDepthZ = depthZ;
        mReverse = reverse;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);

        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;

        final Matrix matrix = t.getMatrix();

        camera.save();
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }
        camera.rotateY(degrees);
        camera.getMatrix(matrix);
        camera.restore();

        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

6 属性动画

6.1 属性动画

///平移
//先移动到10,然后继续向右移动到50,在向左移动到20,最后移动到100的位置
ObjectAnimator oa = ObjectAnimator.ofFloat(imageView, "translationX", 10, 50, 20, 100);
oa.setDuration(2000);
oa.start(); //开始动画

///旋转
ObjectAnimator oa = ObjectAnimator.ofFloat(imageView, "rotationX", 0, 180, 90, 360);
oa.setDuration(2000);
oa.start();

///透明度
ObjectAnimator oa = ObjectAnimator.ofFloat(imageView, "alpha", 0, 0.5f, 0, 1,0,1);
oa.setDuration(2000);
oa.start();

///缩放
ObjectAnimator oa = ObjectAnimator.ofFloat(imageView, "scaleY", 0, 0.5f, 0, 1,0,1);
oa.setDuration(2000);
oa.start();

///动画并行或者串行
AnimatorSet as = new AnimatorSet();
ObjectAnimator oa = ObjectAnimator.ofFloat(imageView, "translationX", 10, 50, 20, 100);
ObjectAnimator oa2 = ObjectAnimator.ofFloat(imageView, "scaleY", 0.1f, 2, 1, 2);
ObjectAnimator oa3 = ObjectAnimator.ofFloat(imageView, "alpha", 0, 0.5f, 0, 1);
ObjectAnimator oa4 = ObjectAnimator.ofFloat(imageView, "rotationY", 0, 180, 90, 360);
as.setDuration(2000);//执行动画时长
as.setTarget(iv);//iv执行动画
//往集合中添加动画
//动画逐个执行
as.playSequentially(oa, oa2, oa3, oa4);
//动画全部一起执行
//as.playTogether(oa, oa2, oa3, oa4);
as.start();

///动画组合
after(Animator anim)   将现有动画插入到传入的动画之后执行
after(long delay)   将现有动画延迟指定毫秒后执行
before(Animator anim)   将现有动画插入到传入的动画之前执行
with(Animator anim)   将现有动画和传入的动画同时执行

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);  
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
AnimatorSet animSet = new AnimatorSet();  
animSet.play(rotate).with(fadeInOut).after(moveIn);  
animSet.setDuration(5000);  
animSet.start();  

///监听
anim.addListener(new AnimatorListener() {  
    @Override  
    public void onAnimationStart(Animator animation) {  
    }  

    @Override  
    public void onAnimationRepeat(Animator animation) {  
    }  

    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  

    @Override  
    public void onAnimationCancel(Animator animation) {  
    }  
});  

anim.addListener(new AnimatorListenerAdapter() {    ///这个是基类,对所有方法提供了一个空实现,方便
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
}); 

///取消,清除等

在xml里设置属性动画

//java代码加载动画
ObjectAnimator oa = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.oanimator);
//设置执行目标
oa.setTarget(iv);
oa.start();//开始执行

res/animator下
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android" >
    <objectAnimator 
        android:propertyName="translationX"
        android:duration="2000"
        android:valueFrom="10"
        android:valueTo="100"
        ></objectAnimator>
</animator>


<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>  


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"  
    android:ordering="sequentially" >  

    <objectAnimator  
        android:duration="2000"  
        android:propertyName="translationX"  
        android:valueFrom="-500"  
        android:valueTo="0"  
        android:valueType="floatType" >  
    </objectAnimator>  

    <set android:ordering="together" >  
        <objectAnimator  
            android:duration="3000"  
            android:propertyName="rotation"  
            android:valueFrom="0"  
            android:valueTo="360"  
            android:valueType="floatType" >  
        </objectAnimator>  

        <set android:ordering="sequentially" >  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="1"  
                android:valueTo="0"  
                android:valueType="floatType" >  
            </objectAnimator>  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="0"  
                android:valueTo="1"  
                android:valueType="floatType" >  
            </objectAnimator>  
        </set>  
    </set>  

</set>  

ValueAnimator的用法:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        float currentValue = (float) animation.getAnimatedValue();  
        Log.d("TAG", "cuurent value is " + currentValue);  ///0到1
    }  
});  
anim.start();  

从0100平滑过渡
<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="intType"/>  

常用属性:

setStartDelay():设置动画延迟播放的时间
setRepeatCount():动画循环播放的次数
setRepeatMode():循环播放的模式,RESTARTREVERSE,重新播放和倒序播放

相比ValueAnimator,ObjectAnimator直接提供了调用属性set方法的功能

ViewPropertyAnimator:这个是对ObjectAnimator的封装
为啥需要这个?因为3.0引入的属性动画已经不是针对View设计的了,虽然我们一般也就用于View
但就算用ObjectAnimator还是略显复杂,所以3.1引入了这个ViewPropertyAnimator

textview.animate().alpha(0f);  
textview.animate().x(500).y(500);  
textview.animate().x(500).y(500).setDuration(5000);  
textview.animate().x(500).y(500).setDuration(5000).setInterpolator(new BounceInterpolator());  


使用ViewPropertyAnimator时,我们自始至终没有调用过start()方法,这是因为新的接口中使用了隐式启动动画的功能,
只要我们将动画定义完成之后,动画就会自动启动。并且这个机制对于组合动画也同样有效,只要我们不断地连缀新的方法,
那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。当然如果
不想使用这一默认机制的话,我们也可以显式地调用start()方法来启动动画

6.2 Path动画

https://my.oschina.net/ososchina/blog/677307 http://m.2cto.com/kf/201503/380377.html

看看效果吧先,Path动画就是让控件跟着一个Path指定的路径走

7 Material动画

安卓L的Material新增了几种动画:

  • Touch feedback(触摸反馈)
  • Reveal effect(揭露效果)
  • Activity transitions(Activity转换效果)---下一节
  • Curved motion(曲线运动)----就是path动画
  • View state changes (视图状态改变)
  • Animate Vector Drawables(可绘矢量动画)

7.1 Touch feedback

  • 最明显,最具代表性的就是波纹动画
  • 使用了Material主题后,波纹动画会自动应用在所有的控件上
  • 可以来设置其属性来调整到我们需要的效果

android:colorControlHighlight:设置波纹颜色 android:colorAccent:设置checkbox等控件的选中颜色 colorAccent设置在values/style.xml下 android:colorControlHighlight设置在values-21/style.xml下

7.2 Reveal effect

btn2.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v) {

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            Animator animator = null;
            animator = ViewAnimationUtils.createCircularReveal(
                    btn2,
                    0,
                    0,
                    0,
                    (float) Math.hypot(btn2.getWidth(), btn2.getHeight()));
            animator.setInterpolator(new AccelerateDecelerateInterpolator());
            animator.setDuration(2000);
            animator.start();
        }else{
            Toaster.toastShort("只支持5.0及其以上系统");
        }

    }
});

7.3 Curved motion

Path动画

7.4 View state changes

View状态,和之前设置的selector是一样的:

android:state_activated      State value for StateListDrawable,
                             set when a view or its parent has been "activated"
                             meaning the user has currently marked it as being
                             of interest.
android:state_active         State value for StateListDrawable.
                             display a check mark.
android:state_checked        State identifier indicating that the object is
                             currently checked.
android:state_enabled        State value for StateListDrawable, set when a view is enabled.
android:state_first          State value for StateListDrawable.
android:state_focused        State value for StateListDrawable, set when a view has input focus.
android:state_last           State value for StateListDrawable.
android:state_middle         State value for StateListDrawable.
android:state_pressed        State value for StateListDrawable, set when the user is pressing down in a view.
android:state_selected       State value for StateListDrawable, set when a view (or one of its parents)
                             is currently selected.
android:state_single         State value for StateListDrawable.
android:state_window_focused

1 传统的selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:state_pressed="true">
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle" >
            <corners android:radius="3dp"/>
            <solid android:color="@color/text_menu1_pressed" />
        </shape>

    </item>

    <item>
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle" >
            <corners android:radius="3dp"/>
            <solid android:color="@color/text_menu1_normal" />
        </shape>

    </item>

</selector>

2 StateListAnimator

这是个动画,5.0提供,可以用来设置状态改变时的动画

放在res/anim里

sel_anim_1.xml

<!-- animate the translationZ property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true">
    <set>
      <objectAnimator android:propertyName="translationZ"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueTo="2dp"
        android:valueType="floatType"/>
        <!-- you could have other objectAnimator elements
             here for "x" and "y", or other properties -->
    </set>
  </item>
  <item android:state_enabled="true"
    android:state_pressed="false"
    android:state_focused="true">
    <set>
      <objectAnimator android:propertyName="translationZ"
        android:duration="100"
        android:valueTo="0"
        android:valueType="floatType"/>
    </set>
  </item>
</selector>

代码加载:

StateListAnimator stateLAnim = AnimatorInflater.loadStateListAnimator(this,R.anim.sel_anim_1);
tv_elevation.setStateListAnimator(stateLAnim);

<Button
    android:id="@+id/btn2"
    android:layout_width="match_parent"
    android:layout_margin="20dp"
    android:layout_height="150dp"
    android:colorControlHighlight="#ff0000"
    android:colorAccent="#00ff00"
    android:text="StateListAnimator"
    android:background="@drawable/sel_menu3"
    android:stateListAnimator="@anim/sel_anim_1"
    />

如何在低版本实现相关的这种动画呢?

http://stackoverflow.com/questions/30545379/android-statelistanimator-in-pre-lollipop 这里给出了答案

也就是这个库实现的原理: https://github.com/ZieIony/Carbon

3 AnimatedStateListDrawable

这个效果有点意思

  • 当你是pressed状态的时候animation-list正着走一遍,drawable使用最后一个。
  • 当你是default状态时animation-list反着走一遍,drawable使用第一个。

放在drawable里

sel_anim_drawable_1.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- provide a different drawable for each state -->
    <item
        android:id="@+id/pressed"
        android:drawable="@drawable/btn_pressed"
        android:state_pressed="true"/>
    <!-- <item
        android:id="@+id/focused"
        android:drawable="@drawable/btn_focused"
        android:state_focused="true"/> -->
    <item
        android:id="@id/default1"
        android:drawable="@drawable/btn_default"/>

    <!-- specify a transition -->
    <transition
        android:fromId="@+id/default1"
        android:toId="@+id/pressed" >
        <animation-list>
            <item
                android:drawable="@drawable/con_time_tk"
                android:duration="500"/>
            <item
                android:drawable="@drawable/btn_default"
                android:duration="500"/>
            <item
                android:drawable="@drawable/btn_focused"
                android:duration="500"/>
            <item
                android:drawable="@drawable/btn_pressed"
                android:duration="500"/>
            <item
                android:drawable="@drawable/con_time_xm"
                android:duration="500"/>
            <item
                android:drawable="@drawable/con_time_tk"
                android:duration="500"/>
        </animation-list>
    </transition>

</animated-selector>

7.5 Animate Vector Drawables

这里有个话题:

http://blog.csdn.net/tianjian4592/article/details/44733123/ http://blog.csdn.net/cym492224103/article/details/41677825 http://www.open-open.com/lib/view/open1416279313977.html svg2Path: http://blog.csdn.net/tianjian4592/article/details/44733123/

VectorDrawable: 大体上是这样:

首先在drawable目录下,有vector定义,vector和svg可以互相转化,也可以转化成Path对象来drawPath

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:height="64dp"
        android:width="64dp"
        android:viewportHeight="600"
        android:viewportWidth="600" >
  <group
      android:name="rotationGroup"
      android:pivotX="300.0"
      android:pivotY="300.0"
      android:rotation="45.0" >
    <path
        android:name="v"
        android:fillColor="#000000"
        android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
  </group>
</vector>

这个是一个比较简单的图形,复杂的svg图数据很多
然后定义对这个vector,也就是矢量图的变换:即AnimatedVectorDrawable
放在drawable目录下:avd.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
                 android:drawable="@drawable/vectordrawable" >
  <target
      android:name="rotationGroup"
      android:animation="@anim/rotation" />
  <target
      android:name="v"
      android:animation="@anim/path_morph" />
</animated-vector>

使用这个AnimatedVectorDrawable
 <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="@dimen/margin"
      android:text="@string/example_from_documentation"
      android:drawableBottom="@drawable/avd"/>

8 Transition

http://blog.jobbole.com/62601/ http://www.open-open.com/lib/view/open1477879867267.html

Transiton是在5.0之后,也就是21,也就是Build.VERSION_CODES.LOLLIPOP引入的
之前的版本只能用Activity的overridePendingTransition()和Fragment的setCustomAnimation()来实现Activity或者Fragment之间的动画切换

  • 关于Activity的overridePendingTransition()和Fragment的setCustomAnimation()

    • 是将整个页面一起动画变换,比较生硬
    • 但是用起来比较简单
    • 而且动画太多,Transition哪儿哪儿都特别华丽,应用又会显得华而不实
    • 这里面有个度
  • 关于Transition

    • 界面A打开界面B,Transition可以让A和B之间有一定的关联性
    • 可能是A和B共享同一个元素
    • 或者B动画开始的位置是基于我们在A中设置的值

7.1 主题设置

要使用Transition进行Activity之间的动画变换时,首先需要设置:

//设置允许通过ActivityOptions.makeSceneTransitionAnimation发送或者接收Bundle
getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
//设置使用TransitionManager进行动画,不设置的话系统会使用一个默认的TransitionManager
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

也可以像ApiDemo里那样设置theme:

<style name="ActivityTransitionTheme" parent="android:Theme.Material" tools:targetApi="lollipop">
    <item name="android:windowEnterTransition">@transition/explode</item>
    <item name="android:windowExitTransition">@transition/explode</item>
    <item name="android:windowSharedElementEnterTransition">@transition/move_image</item>
    <item name="android:windowSharedElementExitTransition">@transition/move_image</item>
    <item name="android:windowAllowReturnTransitionOverlap">true</item>
    <item name="android:windowAllowEnterTransitionOverlap">false</item>
</style>

<activity android:name=".transition.ActivityTransition"
    android:label="Animation/Activity Transition"
    android:theme="@style/ActivityTransitionTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.SAMPLE_CODE" />
    </intent-filter>
</activity>

7.2 动画设置

  • Transition有4种变换:如果从A打开B
    • 退出(exit) A.getWindow().setExitTransition(new Fade());
    • 进入(enter) B.getWindow().setEnterTransition(new Slide());
    • 返回(return) 此时再从B返回到A,B此时又有个动画,B.getWindow().setReturnTransition(new Fade());
    • 再次进入(reenter) 回到A了,A又有个动画,A.getWindow().setReenterTransition(new Explode());

7.3 动画效果

  • 包括:
    • Fade(淡出)
    • Explode(分解)
    • Slide(滑动)
    • 系统会默认为其设置Fade动画效果

7.4 打开界面

//不传参数
Intent intent = new Intent(MainActivityA.this, MainActivityB.class);
ActivityOptions option = ActivityOptions.makeSceneTransitionAnimation(MainActivityA.this);
startActivity(intent, option.toBundle());

//传参数
Intent intent = new Intent(this, ActivityTransitionDetails.class);
intent.putExtra(KEY_ID, v.getTransitionName());
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(this, mHero, "hero"); //mHero是ImageView
startActivity(intent, activityOptions.toBundle());

7.5 第一种效果:直接Transition

行了,到这罗列了不少东西了,该看看效果了

界面1:
getWindow().setExitTransition(new Fade());
getWindow().setReenterTransition(new Explode());
getWindow().setEnterTransition(new Slide());
getWindow().setReturnTransition(new Fade());


界面2:
getWindow().setExitTransition(new Fade());
getWindow().setReenterTransition(new Explode());
getWindow().setEnterTransition(new Slide());
getWindow().setReturnTransition(new Fade());

上图:

差不多能看出来,Transition框架会把布局切割成单独的部分,然后对每一个部分应用Fade,Explode,Slide效果

  • 大体是怎么切割出单独部分的呢?
    • 应该是从DecoreView开始的
    • 只要有ViewGroup包的,就往下切割
    • 直到切割到一个ViewGroup下面不会再有ViewGroup,这一组就会成为一个单独的部分
    • 和ViewGroup平级的也可能是一个View,这个View也会成为一个单独的部分
    • 例如LinearLayout包含RelativeLayout和一个ImageView,RelativeLayout下只有View
    • 这个RelativeLayout和ImageView会被切割成两个单独的部分

7.6 第二种效果:共享元素变换

界面A打开界面B,A中的一个ImageView会在B中放大显示,这就是ApiDemo里提供的例子

这个效果跟上面的Slide,Fade,Explode是不冲突的,挺好

第一步:
在A和B的布局文件中,给两个ImageView的transitionName属性设置成一样的值,这是必须的
android:transitionName="ducky"

第二步:
打开界面时,在ActivityOptions.makeSceneTransitionAnimation中将需要共享的元素作为参数传递过去

Intent intent = new Intent(MainActivityA.this, MainActivityB.class);
ActivityOptions option = ActivityOptions.makeSceneTransitionAnimation(MainActivityA.this,imageView,"ducky");
startActivity(intent, option.toBundle());

ActivityOptions.makeSceneTransitionAnimation(MainActivityA.this,imageView,"ducky");
这里的参数2和3表示:transitionName值为ducky的控件需要共享,在A中就是imageView,在B中你也能根据transitionName找到

//多个元素共享:
ActivityOptions option = ActivityOptions.makeSceneTransitionAnimation(MainActivityA.this,
            Pair.create(imageView, "share_image"),
            Pair.create(button, "share_button"));

效果就是:A中的ImageView会从自己的位置和大小,通过平移和缩放定位到B中对应的位置

看图:

9 setLayoutAnimation:布局动画

一看是Animation,就知道是tween动画

Animation a = getLayoutAnimation();
a.setDuration(1000);
LayoutAnimationController lc = new LayoutAnimationController(a);
wrapper.setLayoutAnimation(lc);

只能控制addView,管不了removeView,也管不了visibility变化,也管不了子控件布局位置变化

能用于所有类型的ViewGroup

10 Transition框架

自Android 4.4(KitKat)起,Android加入了Android Transition框架, 本例使用了一些5.0加入的API,SDK版本请使用21

http://blog.jobbole.com/62601/

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0322/2631.html

http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0113/2310.html

9 LayoutTransition:布局动画

4.0引入,这个不用考虑兼容问题了,都能用

使用:

  • 在ViewGroup的布局中指定:android:animateLayoutChanges="true",可以激活默认动画
  • 也可以在如下代码设置:
ViewGroup  container = (ViewGroup) findViewById(R.id.container);
LayoutTransition transition = new LayoutTransition();
container.setLayoutTransition(transition);

激活时机:

  • addView
  • removeView
  • childView的Visibility变化
  • childView在父布局位置的变化