Android动画<第三篇>:属性动画

前面,我们介绍了视图动画,也叫 Tween (补间)动画,它通常实现透明度动画旋转动画缩放动画位移动画这四种动画,功能比较简单,可实现的动画效果也比较少,这种动画还有一种弊端:
补间动画执行之后并未改变 View 的真实布局属性值。切记这一点,譬如我们在 Activity 中有一个 Button 在屏幕上方, 我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态在屏幕下方,这时如果点击屏幕下方动画执行之后的 Button 是没有任何反应的, 而点击原来屏幕上方没有 Button 的地方却响应的是点击Button的事件。

从 Android 3.0 就推出了属性动画属性动画可以实现补间动画的一切,而且属性动画是没有上述的弊端的。

(1)属性动画原理

原理:修改控件的属性值实现的动画。

(2)常用类

本人所使用的API版本是28,发现一些动画接口被标志成隐藏接口,那么其它未被隐藏的接口有AnimatorSetValueAnimatorStateListAnimatorObjectAnimatorTimeAnimator,他们的继承关系如下:

图片.png

和视图动画一样,所有的属性动画都有一个共有的父类Animator。

(3)ValueAnimator的使用

ValueAnimator是属性动画中最核心的一个类,它通过不断控制值的变化,再不断赋给对象的属性,从而实现动画效果。

一、【如何创建ValueAnimator对象】

创建ValueAnimator对象很简单,直接new即可,如果仅仅是这样的话我也不会单独拉出来讲解。
使用ValueAnimator的of系列创建对象:

  • ofInt:构造并返回在int值之间设置动画的ValueAnimator
  • ofArgb:构造并返回在颜色值之间设置动画的ValueAnimator
  • ofFloat:构造并返回在浮点值之间进行动画处理的ValueAnimator
  • ofPropertyValuesHolder:构造并返回在值之间进行动画处理的ValueAnimator
  • ofObject:构造并返回在对象值之间设置动画的ValueAnimator

以上5种创建ValueAnimator对象的方法这里不需要介绍,我想贴出它们的源码大家就都一目了然了。

public static ValueAnimator ofInt(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    return anim;
}


public static ValueAnimator ofArgb(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    anim.setEvaluator(ArgbEvaluator.getInstance());
    return anim;
}


public static ValueAnimator ofFloat(float... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setFloatValues(values);
    return anim;
}


public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setValues(values);
    return anim;
}


public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setObjectValues(values);
    anim.setEvaluator(evaluator);
    return anim;
}

二、举例

我们就拿缩放动画举例,大家仔细看一下代码中的注释。

    //创建ValueAnimator对象,并设置属性动画的值(初始值、过程值、结束值)
    ValueAnimator anim = ValueAnimator.ofFloat(1f, 2.0f);

    //动画执行时间
    anim.setDuration(2000);

    //动画播放延迟时间
    anim.setStartDelay(500);

    //设置动画重复次数
    //假如设置重复次数为n,那么动画总共执行的次数为n+1次
    //如果设置次数为ValueAnimator.INFINITE,那么动画则无限循环
    anim.setRepeatCount(1);

    //设置重复模式
    //RESTART:正序播放
    //REVERSE:倒序播放
    anim.setRepeatMode(ValueAnimator.REVERSE);

    //监听动画更新
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {

            float currentValue = (float) animation.getAnimatedValue();

            Log.d("aaa", "当前值:"+currentValue);

            //手动设置参数
            iv_ani.setScaleX(currentValue);
            iv_ani.setScaleY(currentValue);

            //重绘视图
            iv_ani.requestLayout();

        }
    });
    anim.start();

xml写法如下:

图片.png



    // 载入XML动画
    ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(MainActivity.this, R.animator.set_animation);

    // 设置动画对象
    animator.setTarget(iv_ani);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float currentValue = (float) animation.getAnimatedValue();

            Log.d("aaa", "当前值:"+currentValue);

            //手动设置参数
            iv_ani.setScaleX(currentValue);
            iv_ani.setScaleY(currentValue);

            //重绘视图
            iv_ani.requestLayout();
        }
    });
    // 启动动画
    animator.start();

效果如下:

71.gif

再来看一下属性值变化:

72.gif

补充: xml属性讲解

android:valueFrom="1"   // 初始值
android:valueTo="2"     // 结束值
android:valueType="floatType" // 变化值类型 :floatType & intType
android:duration="2000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="500" // 动画延迟开始时间(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" // 重放次数,值为infinite时表示无限循环
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度

以上列子已经基本介绍完Java代码和xml代码的用法了,接下来的例子中将会简化写法。

根据以上的列子,您可能已经总结出属性动画实现的步骤了:

[一]: 创建ValueAnimator对象,并确定取值类型和取值范围;
[二]: 设置动画执行时间;(动画延迟时间和重复次数可选)
[三]: 添加属性值变化监听,上面的动态图打印了属性值的变化,属性值的变化受到动画执行时间的影响;
[四]: 利用属性值手动改变view的状态;
[五]: 重绘view(相当于执行了view的onDraw方法)

此外,您可能还发现,属性动画动的只是属性值,属性值在不停的变化,我们需要做的就是拿到这个变化的属性值,拿到这个正在变化的属性值就可以随意操作view了,想怎么操作就怎么操作。

以上举例的属性值类型是float类型,float和int类似,所以int就不举例了,下面来说一说ofArgb的使用:

            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                anim = ValueAnimator.ofArgb(0xffffffff,0x00000000);
            }

ofArgb是Android5.0新增的接口,支持带透明度的颜色取值,那么为什么不使用ofInt和ofFloat呢?

因为颜色值的变化和ofInt、ofFloat不同,我们不能用常规的方法评估颜色值,ofArgb加入了估值器:ArgbEvaluator,利用特定的算法评估颜色的取值,这里颜色的取值就是颜色矩阵的取值,源码如下:

public Object evaluate(float fraction, Object startValue, Object endValue) {
    int startInt = (Integer) startValue;
    float startA = ((startInt >> 24) & 0xff) / 255.0f;
    float startR = ((startInt >> 16) & 0xff) / 255.0f;
    float startG = ((startInt >>  8) & 0xff) / 255.0f;
    float startB = ( startInt        & 0xff) / 255.0f;

    int endInt = (Integer) endValue;
    float endA = ((endInt >> 24) & 0xff) / 255.0f;
    float endR = ((endInt >> 16) & 0xff) / 255.0f;
    float endG = ((endInt >>  8) & 0xff) / 255.0f;
    float endB = ( endInt        & 0xff) / 255.0f;

    // convert from sRGB to linear
    startR = (float) Math.pow(startR, 2.2);
    startG = (float) Math.pow(startG, 2.2);
    startB = (float) Math.pow(startB, 2.2);

    endR = (float) Math.pow(endR, 2.2);
    endG = (float) Math.pow(endG, 2.2);
    endB = (float) Math.pow(endB, 2.2);

    // compute the interpolated color in linear space
    float a = startA + fraction * (endA - startA);
    float r = startR + fraction * (endR - startR);
    float g = startG + fraction * (endG - startG);
    float b = startB + fraction * (endB - startB);

    // convert back to sRGB in the [0..255] range
    a = a * 255.0f;
    r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
    g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
    b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

    return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}

ofArgb估值器已被SDK封装,不需要我们手动设置,但是ofObject需要我们去手动设置估值器

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

TypeEvaluator就是估值器,所谓估值器就是决定具体变化的数值。

关键代码如下:

public class ScaleEvaluator implements TypeEvaluator {

    @Override
    public Float evaluate(float fraction, Object startValue, Object endValue) {

        float start = (float) startValue;
        float end = (float) endValue;
        return Math.abs(end-start) * (float) Math.sin(Math.PI * fraction) + start;
    }
}


            //创建ValueAnimator对象,并设置属性动画的值(初始值、过程值、结束值)
            ValueAnimator anim = ValueAnimator.ofObject(new ScaleEvaluator(),1.0f, 2.0f);

            //动画执行时间
            anim.setDuration(10000);

            //动画播放延迟时间
            anim.setStartDelay(500);

            //设置动画重复次数
            //假如设置重复次数为n,那么动画总共执行的次数为n+1次
            //如果设置次数为ValueAnimator.INFINITE,那么动画则无限循环
            anim.setRepeatCount(1);

            //设置重复模式
            //RESTART:正序播放
            //REVERSE:倒序播放
            anim.setRepeatMode(ValueAnimator.REVERSE);

            //监听动画更新
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {

                    float currentValue = (float) animation.getAnimatedValue();

                    Log.d("aaa", "当前值:"+currentValue);

                    //手动设置参数
                    iv_ani.setScaleX(currentValue);
                    iv_ani.setScaleY(currentValue);

                    //重绘视图
                    iv_ani.requestLayout();

                }
            });
            anim.start();

效果如下:

73.gif

除了ofInt、ofFloat、ofArgb、ofObject可以创建ValueAnimator对象之外,ofPropertyValuesHolder同样可以创建ValueAnimator对象。

ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)

这个方法的参数是PropertyValuesHolder对象,所以我们需要创建PropertyValuesHolder对象,创建PropertyValuesHolder对象的方法有很多,如图:

图片.png

这里唯一需要介绍的是Keyframe的使用,代码如下:

            Keyframe keyframe1 = Keyframe.ofFloat(0f, 1f);
            Keyframe keyframe2 = Keyframe.ofFloat(0.1f, 1.1f);
            Keyframe keyframe3 = Keyframe.ofFloat(0.2f, 1.2f);
            Keyframe keyframe4 = Keyframe.ofFloat(0.3f, 1.3f);
            Keyframe keyframe5 = Keyframe.ofFloat(0.4f, 1.4f);
            Keyframe keyframe6 = Keyframe.ofFloat(0.5f, 1.5f);
            Keyframe keyframe7 = Keyframe.ofFloat(0.6f, 1.6f);
            Keyframe keyframe8 = Keyframe.ofFloat(0.7f, 1.7f);
            Keyframe keyframe9 = Keyframe.ofFloat(0.8f, 1.8f);
            Keyframe keyframe10 = Keyframe.ofFloat(0.9f, 1.9f);
            Keyframe keyframe11 = Keyframe.ofFloat(1.0f, 2f);
            PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofKeyframe("aaa", keyframe1, keyframe2, keyframe3, keyframe4, keyframe5, keyframe6, keyframe7, keyframe8, keyframe9, keyframe10, keyframe11);
            //创建ValueAnimator对象,并设置属性动画的值(初始值、过程值、结束值)
            ValueAnimator anim = ValueAnimator.ofPropertyValuesHolder(propertyValuesHolder);

Keyframe.ofFloat(0f, 1f),创建一个关键帧 对象,第一个参数代表动画进度[0, 1],第二个参数代表当前进度下的取值。

(4)ObjectAnimator的使用

ObjectAnimatorValueAnimator的子类,那么为什么ObjectAnimator会单独拎出来,并作为ValueAnimator子类的存在。我们先说说ObjectAnimatorValueAnimator之间的区别。

ValueAnimator:通过监听属性值的变化,并手动修改view的属性;
ObjectAnimator:设置属性名称,自动修改view的属性;

有关ObjectAnimator的方法有多个,如图:

图片.png

和ValueAnimator的区别是,ObjectAnimator需要将view和propertyName作为参数。

代码如下:

            //创建ObjectAnimator对象,并设置属性动画的值(初始值、过程值、结束值)
            ObjectAnimator anim = ObjectAnimator.ofFloat(iv_ani, "scaleX", 1.0f, 2.0f);

            //动画执行时间
            anim.setDuration(10000);

            //动画播放延迟时间
            anim.setStartDelay(500);

            //设置动画重复次数
            //假如设置重复次数为n,那么动画总共执行的次数为n+1次
            //如果设置次数为ValueAnimator.INFINITE,那么动画则无限循环
            anim.setRepeatCount(1);

            //设置重复模式
            //RESTART:正序播放
            //REVERSE:倒序播放
            anim.setRepeatMode(ValueAnimator.REVERSE);
            
            anim.start();

效果如下:

74.gif

那么,propertyName是怎么知道是什么值的呢?

其实propertyName=“scaleX”就相当于如下代码:

iv_ani.setScaleX(xxx);

如图所示,绿色框框出来的就是属性名称了,属性名称第一个字母大小写随意,非第一个字母不可任意修改大小写。

图片.png

其他属性名称也是通过以上方法得到

图片.png

您可能会想到除了缩放动画之外,还有平移、旋转、透明度相关动画,他们有自己的属性名称,部分属性名称请看下表:

属性 作用 数值类型
Alpha 控制View的透明度
translationX 控制X方向的位移 float
translationY 控制Y方向的位移 float
scaleX 控制X方向的缩放倍数 float
scaleY 控制Y方向的缩放倍数 float
Rotation 控制以屏幕方向为轴的旋转度数 float
RotationX 控制以X轴为轴的旋转度数 float
RotationY 控制以Y轴为轴的旋转度数 float
(5)TimeAnimator使用

TimeAnimatorValueAnimator的子类,它没有提供ofArgbofIntofFloatofObject之类的接口,没有提供设置属性值的接口,它的使用方式如下:

            TimeAnimator anim = new TimeAnimator();
            anim.setDuration(2000);
            anim.setTimeListener(new TimeAnimator.TimeListener() {
                @Override
                public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {

                    if(totalTime > animation.getDuration()){
                        animation.end();
                    }

                    Log.d("aaa", "totalTime:"+totalTime+"----"+"deltaTime:"+deltaTime);

                    iv_ani.setScaleX(totalTime/1000.0f);
                    iv_ani.setScaleY(totalTime/1000.0f);

                    //重绘视图
                    iv_ani.requestLayout();

                }
            });

            anim.start();

效果如下:

75.gif

TimeAnimator是一个事件动画,通过回调方法onTimeUpdate修改view的属性达到动画效果,这个回调方法每16ms或者17ms执行一次,与ValueAnimator相比,TimeAnimator的动画监听多了两个参数:totalTime、deltaTime,totalTime是动画执行的总时间,deltaTime是动画执行的间隔时间。

(6)AnimatorSet的使用

首先,新建两个动画

    ObjectAnimator anim1 = ObjectAnimator.ofFloat(iv_ani, "rotationY", 180.0f);
    anim1.setRepeatMode(ValueAnimator.REVERSE);
    anim1.setRepeatCount(1);


    ObjectAnimator anim2 = ObjectAnimator.ofFloat(iv_ani, "rotationX", 180.0f);
    anim2.setRepeatMode(ValueAnimator.REVERSE);
    anim2.setRepeatCount(1);

anim1的动画效果是:

76.gif

anim2的动画效果是:

77.gif

现在,将这两个动画组合成一个动画:

[方式一] anim1先执行anim2后执行

思路:通过AnimatorSet来组合两个动画,组合动画的关键代码如下:

animatorSet.play(anim2).after(anim1);

animatorSet.play(anim1).before(anim2);

具体实现代码如下:

    ObjectAnimator anim1 = ObjectAnimator.ofFloat(iv_ani, "rotationY", 180.0f);
    anim1.setRepeatMode(ValueAnimator.REVERSE);
    anim1.setRepeatCount(1);

    ObjectAnimator anim2 = ObjectAnimator.ofFloat(iv_ani, "rotationX", 180.0f);
    anim2.setRepeatMode(ValueAnimator.REVERSE);
    anim2.setRepeatCount(1);

    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.setDuration(4000);
    animatorSet.play(anim2).after(anim1);
    animatorSet.start();

效果如下:

78.gif

[方式二] anim1和anim2同时执行

关键代码如下:(with将两个动画连接)

animatorSet.play(anim1).with(anim2);

关键代码如下:

    ObjectAnimator anim1 = ObjectAnimator.ofFloat(iv_ani, "rotationY", 180.0f);
    anim1.setRepeatMode(ValueAnimator.REVERSE);
    anim1.setRepeatCount(1);

    ObjectAnimator anim2 = ObjectAnimator.ofFloat(iv_ani, "rotationX", 180.0f);
    anim2.setRepeatMode(ValueAnimator.REVERSE);
    anim2.setRepeatCount(1);

    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.setDuration(4000);
    animatorSet.play(anim1).with(anim2);
    animatorSet.start();

执行效果如下:

79.gif

以上两种方式,第一种顺序执行动画(串行),第二种几个动画一起执行(并行),AnimatorSet有两个方法可以实现多个动画的串行和并行执行。

//串行
public void playSequentially(Animator... items)
//并行
public void playTogether(Animator... items)

playSequentially的执行效果和方式一一样,playTogether的执行效果和方式二一样。

(7)估值器Evaluators

上面我已经提到了估值器的概念,为了实现理想中的估值,特地去自定义了一个估值器,下面说一下自带的估值器。

[IntEvaluator]

public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
    int startInt = startValue;
    return (int)(startInt + fraction * (endValue - startInt));
}

[FloatEvaluator]

public Float evaluate(float fraction, Number startValue, Number endValue) {
    float startFloat = startValue.floatValue();
    return startFloat + fraction * (endValue.floatValue() - startFloat);
}

[ArgbEvaluator]

public Object evaluate(float fraction, Object startValue, Object endValue) {
    int startInt = (Integer) startValue;
    float startA = ((startInt >> 24) & 0xff) / 255.0f;
    float startR = ((startInt >> 16) & 0xff) / 255.0f;
    float startG = ((startInt >>  8) & 0xff) / 255.0f;
    float startB = ( startInt        & 0xff) / 255.0f;

    int endInt = (Integer) endValue;
    float endA = ((endInt >> 24) & 0xff) / 255.0f;
    float endR = ((endInt >> 16) & 0xff) / 255.0f;
    float endG = ((endInt >>  8) & 0xff) / 255.0f;
    float endB = ( endInt        & 0xff) / 255.0f;

    // convert from sRGB to linear
    startR = (float) Math.pow(startR, 2.2);
    startG = (float) Math.pow(startG, 2.2);
    startB = (float) Math.pow(startB, 2.2);

    endR = (float) Math.pow(endR, 2.2);
    endG = (float) Math.pow(endG, 2.2);
    endB = (float) Math.pow(endB, 2.2);

    // compute the interpolated color in linear space
    float a = startA + fraction * (endA - startA);
    float r = startR + fraction * (endR - startR);
    float g = startG + fraction * (endG - startG);
    float b = startB + fraction * (endB - startB);

    // convert back to sRGB in the [0..255] range
    a = a * 255.0f;
    r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
    g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
    b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

    return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}

[FloatArrayEvaluator]

@Override
public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
    float[] array = mArray;
    if (array == null) {
        array = new float[startValue.length];
    }

    for (int i = 0; i < array.length; i++) {
        float start = startValue[i];
        float end = endValue[i];
        array[i] = start + (fraction * (end - start));
    }
    return array;
}

[IntArrayEvaluator]

@Override
public int[] evaluate(float fraction, int[] startValue, int[] endValue) {
    int[] array = mArray;
    if (array == null) {
        array = new int[startValue.length];
    }
    for (int i = 0; i < array.length; i++) {
        int start = startValue[i];
        int end = endValue[i];
        array[i] = (int) (start + (fraction * (end - start)));
    }
    return array;
}

[RectEvaluator]

@Override
public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
    int left = startValue.left + (int) ((endValue.left - startValue.left) * fraction);
    int top = startValue.top + (int) ((endValue.top - startValue.top) * fraction);
    int right = startValue.right + (int) ((endValue.right - startValue.right) * fraction);
    int bottom = startValue.bottom + (int) ((endValue.bottom - startValue.bottom) * fraction);
    if (mRect == null) {
        return new Rect(left, top, right, bottom);
    } else {
        mRect.set(left, top, right, bottom);
        return mRect;
    }
}

[PointFEvaluator]

@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
    float x = startValue.x + (fraction * (endValue.x - startValue.x));
    float y = startValue.y + (fraction * (endValue.y - startValue.y));

    if (mPoint != null) {
        mPoint.set(x, y);
        return mPoint;
    } else {
        return new PointF(x, y);
    }
}

以上就是官方提供的估值器了,如果仍然不满足您的需求,那么就自定义吧。

(8)插值器

默认情况下,动画懂事匀速执行的,那么如果想要变速执行,那么就需要插值器了,官方自带的插值器有:

下面一一展示不同插值器的动画效果:
[默认] 如果不添加插值器,默认是匀速的
[AccelerateDecelerateInterpolator] 先加速后减速
[AccelerateInterpolator] 加速
[DecelerateInterpolator] 减速
[AnticipateOvershootInterpolator] 先向相反方向改变,再加速播放,会超出目标值然后缓慢移动至目标值,类似于弹簧回弹
[BounceInterpolator] 快到目标值时值会跳跃
[CycleIinterpolator] 动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)
[LinearInterpolator] 线性均匀改变
[OvershottInterpolator] 最后超出目标值然后缓慢改变到目标值
[FastOutLinearInInterpolator] 和AccelerateInterpolator类似,两者都是代表不断加速,AccelerateInterpolator是指数曲线递增,FastOutLinearInInterpolator是贝塞尔曲线递增。
[FastOutSlowInInterpolator] 和AccelerateDecelerateInterpolator类似,都是先加速后减速,AccelerateDecelerateInterpolator 用的是正弦 / 余弦曲线,FastOutSlowInInterpolator 用的是贝塞尔曲线。

  • AccelerateDecelerateInterpolator:先加速后减速。
85.gif
  • AccelerateInterpolator:加速。
86.gif
  • DecelerateInterpolator:减速。
87.gif
  • AnticipateInterpolator:先向相反方向改变一段再加速播放。

[
88.gif
  • AnticipateOvershootInterpolator:先向相反方向改变,再加速播放,会超出目标值然后缓慢移动至目标值,类似于弹簧回弹。
80.gif
  • BounceInterpolator:快到目标值时值会跳跃。
89.gif
  • CycleIinterpolator:动画循环一定次数

现在假设需要循环两次:

animatorSet.setInterpolator(new CycleInterpolator(2));

效果如下:

90.gif
  • LinearInterpolator:线性均匀改变。
91.gif
  • OvershootInterpolator:最后超出目标值然后缓慢改变到目标值。
92.gif
(9) ViewPropertyAnimator动画

在 Android API 12 时,View 中添加了 animate 方法,使用方法也比较简单。

iv_ani.animate().alpha(0.5f).scaleX(1.5f).scaleY(1.5f).setDuration(2000);

设置动画的代码是链式的,编码简单,可读性高。

ViewPropertyAnimator的常用方法如下:

方法 描述
alpha(float value) 设置透明度,value表示变化到多少,1不透明,0全透明。
scaleY(float value) 设置Y轴方向的缩放大小,value表示缩放到多少。1表示正常规格。小于1代表缩小,大于1代表放大。
scaleX(float value) 设置X轴方向的缩放大小,value表示缩放到多少。1表示正常规格。小于1代表缩小,大于1代表放大。
translationY(float value) 设置Y轴方向的移动值,作为增量来控制View对象相对于它父容器的左上角坐标偏移的位置,即移动到哪里。
translationX(float value) 设置X轴方向的移动值,作为增量来控制View对象相对于它父容器的左上角坐标偏移的位置。
rotation(float value) 控制View对象围绕支点进行旋转, rotation针对2D旋转
rotationX (float value) 控制View对象围绕X支点进行旋转, rotationX针对3D旋转
rotationY(float value) 控制View对象围绕Y支点进行旋转, rotationY针对3D旋转
x(float value) 控制View对象相对于它父容器的左上角坐标在X轴方向的最终位置。
y(float value) 控制View对象相对于它父容器的左上角坐标在Y轴方向的最终位置
void cancel() 取消当前正在执行的动画
setListener(Animator.AnimatorListener listener) 设置监听器,监听动画的开始,结束,取消,重复播放
setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) 设置监听器,监听动画的每一帧的播放
setInterpolator(TimeInterpolator interpolator) 设置插值器
setStartDelay(long startDelay) 设置动画延长开始的时间
setDuration(long duration) 设置动画执行的时间
withLayer() 设置是否开启硬件加速
withStartAction(Runnable runnable) 设置用于动画监听开始(Animator.AnimatorListener)时运行的Runnable任务对象
withEndAction(Runnable runnable) 设置用于动画监听结束(Animator.AnimatorListener)时运行的Runnable任务对象

以上便是 ViewPropertyAnimator 一些操作方法,其实上面很多属性设置方法都对应着一个By结尾的方法,其变量则代表的是变化量,假设view当前位置是startValue,那么想要移动到endValue位置,则变化量为endValue-startValue

(10)布局动画-LayoutTransition的使用

Property 动画系统还提供了对 ViewGroup 中 View 添加时的动画功能,我们可以用 LayoutTransition 对 ViewGroup 中的 View 进行动画设置显示。LayoutTransition 的动画效果都是设置给 ViewGroup,然后当被设置动画的 ViewGroup 中添加删除 View 时体现出来。 该类用于当前布局容器中有 View 添加、删除、隐藏、显示等时候定义布局容器自身的动画和 View 的动画,也就是说当在一个 LinerLayout 中隐藏一个 View 的时候,我们可以自定义 整个由于 LinerLayout 隐藏 View 而改变的动画,同时还可以自定义被隐藏的 View 自己消失时候的动画等。

我们可以发现 LayoutTransition 类中主要有五种容器转换动画类型,具体如下:

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

一、[系统默认ViewGroup的LayoutTransition动画]

我们可以通过如下方式使用系统提供的默认ViewGroup的LayoutTransition动画:

android:animateLayoutChanges="true"

在 ViewGroup 添加如上 xml 属性默认是没有任何动画效果的,因为前面说了,该动画针对于 ViewGroup 内部东东发生改变时才有效, 所以当我们设置如上属性然后调运 ViewGroup 的 addView、removeView 方法时就能看见系统默认的动画效果了。

首先看一下没有添加以上代码的效果:

93.gif

如果在布局中添加了那句话,如图:

图片.png

其效果是:

94.gif

二、[通过代码实现LayoutTransition动画]

代码如下:

    LayoutTransition layoutTransition = new LayoutTransition();
    layoutTransition.setAnimator(LayoutTransition.APPEARING, layoutTransition.getAnimator(LayoutTransition.APPEARING));
    layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, layoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING));
    layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, layoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING));
    layoutTransition.setAnimator(LayoutTransition.CHANGING, layoutTransition.getAnimator(LayoutTransition.CHANGING));
    layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, layoutTransition.getAnimator(LayoutTransition.DISAPPEARING));

    //  为根布局设置LayoutTransition
    ll_root.setLayoutTransition(layoutTransition);

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

            final Button button = new Button(MainActivity.this);
            button.setBackgroundColor(Color.BLACK);
            button.setTextColor(Color.WHITE);
            button.setText("点击移除view");

            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ll_root.removeView(button);
                }
            });
            ll_root.addView(button);

        }
    });

效果如下:

95.gif
(11)StateListAnimator

这关这个知识点,请看这篇博客

动画<第四篇>:属性动画之StateListAnimator

参考文献:

https://github.com/OCNYang/Android-Animation-Set/tree/master/property-animation

[本章完...]

你可能感兴趣的:(Android动画<第三篇>:属性动画)