Android 属性动画进阶

2020-06-15  本文已影响0人  as_pixar

要散布阳光到别人心里,先得自己心里有阳光。——罗曼·罗兰

PropertyValuesHolder 类的含义是保存属性和对应的值。我们通过ofFloat(Object target, String propertyName, float... values) 构造动画,ofFloat()函数内部其实就是将传入的参数封装成PropertyValuesHolder实例来保存状态。在封装成PropertyValuesHolder实例后,后期的各种操作以PropertyValuesHolder为主的。

创建PropertyValuesHolder实例函数通常有以下几个:

public static PropertyValuesHolder ofFloat(String propertyName, float... values)

public static PropertyValuesHolder ofInt(String propertyName, int... values)

public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
            Object... values) 

public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)

很明显,ObjectAnimator中的ofFloat()函数只比PropertyValuesHolder中的ofFloat()函数多了一个target参数,其它参数完全一样。

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)

在这里,ofFloat(),ofInt()函数创建的是PropertyValuesHolder对象,下一步就是如何将构造的PropertyValuesHolder实例设置到ObjectAnimator中。

ObjectAnimator给我们提供了一个设置PropertyValuesHolder实例的入口

public static ObjectAnimator ofPropertyValuesHolder(Object target,
            PropertyValuesHolder... values)
package com.as.propertyanimator;

import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;

public class AdvanceAnimatorActivity extends AppCompatActivity {

    private TextView tv1;
    private TextView tv2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_advance_animator);

        AppCompatButton btnStart = findViewById(R.id.btnStart);
        tv1 = findViewById(R.id.tv1);
        tv2 = findViewById(R.id.tv2);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                objectAnimator();
            }
        });
    }

    private void objectAnimator() {
        PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("rotation", 60, -60f, 40, -40, -20, 20, 10, 10, 0f);
        PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0.5f, 1, 0.1f, 1f);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tv1, rotationHolder, alphaHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

}

在这里,我们创建了两个PropertyValuesHolder实例,第一个是rotationHolder,使用ofFloat()函数创建,属性值是rotation,对应的是View类中的setRotaion(float rotation)函数,后面传入了很多值,让其左右摆动,第二个是改变alpha的alphaHolder。最后通过ObjectAnimator.ofPropertyValuesHolder()函数将rotationHolder和alphaHolder设置给tv1,构造出ObjectAnimator对象,就可以开始动画了。

PropertyValuesHolder 之 ofObject()

public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
            Object... values)

它的各个参数与ObjectAnimator.ofObject()函数的参数类似,只是少了Target 参数而已

public static ObjectAnimator ofObject(Object target, String propertyName,
            TypeEvaluator evaluator, Object... values)

下面讲一个之前我们写过的例子,字母A变化到Z。这个在ValueAnimator已经讲到了,同时通过自定义CharEvaluator来自动实现字母的改变计算。

package com.as.propertyanimator;

import android.animation.TypeEvaluator;

/**
 * 动画求值器从字母A到字母Z
 */
public class CharEvaluator implements TypeEvaluator<Character> {

    @Override
    public Character evaluate(float fraction, Character startValue, Character endValue) {
        int startInt = startValue; //A = 65
        int endInt = endValue;     //Z = 90
        return (char) (startInt + fraction * (endInt - startInt)); //当前字符
    }

}

从CharEvaluator 产出的动画中间值类型为 Character,TextView 虽然有setText(CharSequence text)函数,这个函数的参数类型是 CharSequence,而不是 Character。所以,我们要自定义一个派生自TextView 的类,来改变TextView 的字符。

package com.as.propertyanimator;

import android.content.Context;
import android.util.AttributeSet;

import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;

public class MyTextView extends AppCompatTextView {

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public void setCharText(Character character){
        setText(String.valueOf(character));
    }
}

在这里,我们定义了一个函数setCharText(Character character),参数类型是Character,对应的属性是CharText;动画中的TextView 其实就是我们自定义的MyTextView。

private void objectAnimator2() {
        PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofObject("CharText", new CharEvaluator(), 'A', 'Z');

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tv1,valuesHolder);
        objectAnimator.setDuration(5000);
        objectAnimator.start();
    }

首先调用PropertyValuesHolder.ofOjbect()函数生成一个PropertyValuesHolder实例,它的属性就是CharText,由于CharEvaluator的中间值是Character 类型,所以对应的set函数setCharText(Character character)。然后使用ObjectAnimator.ofPropertyValuesHolder()函数生成ObjectAnimator实例并开始动画。

Keyframe

前面我们通过插值器TimeInterpolator和Evaluator估值器来控制动画速率,但大部分涉及数学知识,恐怕并不简单。为了方便控制动画速率的问题,Google为了我们定义了一个Keyframe类,直译过来就是关键帧。

关键帧这个概念是从动画中学来的,对于视频而言,一般1秒24帧图片。比如,我们要让一直球在30秒内从(0,0)点运动到(300,300)点。在Flash中,我们只需要定义两个关键帧,在动画开始时定义一个关键帧,把球的位置设定在(0,0)点;在30秒后在定义一个关键帧,把球的位置设定在(300,300)点。在动画开始时球在(0,0)点,在30秒内Adobe Flash 就会自动填充,把球平滑移动到第二个关键帧位置(300,300)点。

所以Google 的Keyframe 也不例外,其生成方式为:

public static Keyframe ofFloat(float fraction, float value)

在了解Keyframe.ofFloat()函数参数以后,我们来看看PropertyValuesHolder是如何使用Keyframe对象的。

public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
        Keyframe keyframe0 = Keyframe.ofFloat(0f, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.25f, -500f);
        Keyframe keyframe2 = Keyframe.ofFloat(1f, 100f);

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("translationY", keyframe0, keyframe1, keyframe2);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

我们让TextView 距离顶部250dp,第一步生成Keyframe对象,第二步,PropertyValuesHolder.ofKeyframe()函数生成PropertyValuesHolder对象,第三步ObjectAnimator.ofPropertyValuesHolder()函数生成对应的Animator。

下面通过模拟动画响铃的例子来讲解Keyframe的使用方法

private void phoneCall() {
        Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.1f, -20);
        Keyframe keyframe2 = Keyframe.ofFloat(0.2f, 20);
        Keyframe keyframe3 = Keyframe.ofFloat(0.3f, -20);
        Keyframe keyframe4 = Keyframe.ofFloat(0.4f, 20);
        Keyframe keyframe5 = Keyframe.ofFloat(0.5f, -20);
        Keyframe keyframe6 = Keyframe.ofFloat(0.6f, 20);
        Keyframe keyframe7 = Keyframe.ofFloat(0.7f, -20);
        Keyframe keyframe8 = Keyframe.ofFloat(0.8f, 20);
        Keyframe keyframe9 = Keyframe.ofFloat(0.9f, -20);
        Keyframe keyframe10 = Keyframe.ofFloat(1f, 0);

        PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofKeyframe("rotation", keyframe0, keyframe1, keyframe2,
                keyframe3, keyframe4, keyframe5, keyframe6,
                keyframe7, keyframe8, keyframe9, keyframe10);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, rotationHolder);
        objectAnimator.setDuration(2000);
        objectAnimator.start();

    }

通过Keyframe 来将电话图片强烈,频繁的翻转,看起来就像电话响铃效果。



首相,定义11个Keyframe指定开始和结束的旋转角度为0,既恢复原状。

Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
Keyframe keyframe10 = Keyframe.ofFloat(1f, 0);

在进度为0.2时,旋转到右边20度位置。进度为0.3时,旋转到左边20度的位置。

Keyframe keyframe2 = Keyframe.ofFloat(0.2f, 20);

根据Keyframe 生成PropertyValuesHolder对象,利用ObjectAnimator.ofPropertyValuesHolder()函数生成ObjectAnimator对象。

ofFloat 与 ofInt

上面我们看到了Keyframe.ofFloat()函数的用法,其实,除了ofFlat()函数以外,Keyframe还有ofInt(),ofObject()这些用于创建Keyframe实例的函数,这里我们着重看看ofFloat与ofInt的构造函数和使用方法。

public static Keyframe ofFloat(float fraction)
public static Keyframe ofFloat(float fraction, float value)

public static Keyframe ofInt(float fraction)
public static Keyframe ofInt(float fraction, int value)

从上面ofFloat和ofInt的构造函数对比可以发现,ofFloat和ofInt的构造函数所需要的参数是非常像的

public static Keyframe ofFloat(float fraction)

这个构造函数比较特殊,只有要给fraction,表示当前关键帧所在的动画进度。那么,在这个进度所要对应的值怎么设置呢?

其实,除了上面的构造函数,Keyframe还有一些常用函数用来设置fraction ,value和interpolator,定义如下:

 public void setFraction(float fraction) {
        mFraction = fraction;
    }

public void setValue(Object value)

在这里,通过setValue 函数可以设置Keyframe 在当前动画进度位置所对应的具体数值。

插值器

Keyframe 也允许我们设置插值器

public void setInterpolator(TimeInterpolator interpolator) {
        mInterpolator = interpolator;
    }

如果给这个Keyframe 设置插值器,那么,在从上一个Keyframe 到当前Keyframe 的中间值计算过程中,使用的就是这个插值器。比如:

         Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.1f, -20);
        keyframe1.setInterpolator(new BounceInterpolator());
        Keyframe keyframe2 = Keyframe.ofFloat(0.2f, 20);
        keyframe2.setInterpolator(new LinearInterpolator());

我们给keyframe1 设置了回弹插值器,那么从keyframe0到keyframe1的中间值计算过程中,使用的就是回弹插值器,对应进度从0到0.1。
同样,我们给keyframe2设置了线性插值器,那么keyframe1到keyframe2的中间值计算过程中,使用的就是线性插值器。

很显然,keyframe0设置插值器是无效的,因为它是第一帧。

示例一,没有插值器

 private void phoneCall1() {
        Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.5f, 100);
        Keyframe keyframe2 = Keyframe.ofFloat(1);
        keyframe2.setValue(0);


        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", keyframe0, keyframe1, keyframe2);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, frameHolder);
        objectAnimator.setDuration(2000);
        objectAnimator.start();
    }

在这段代码中,只有三个关键帧,最后一个Keyframe 的生成方法如下

 Keyframe keyframe2 = Keyframe.ofFloat(1);
 keyframe2.setValue(0);

对于Keyframe而言,fraction 和 value 这两个参数是必须要有的,所以,无论使用哪种方式实例化Keyframe,都必须保证这两个值被初始化。这里没有默认的插值器,使用的是默认的线性插值器(LinearInterpolator)


示例二,使用插值器

private void phoneCall2() {
        Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.5f, 100);
        Keyframe keyframe2 = Keyframe.ofFloat(1);
        keyframe2.setValue(0);
        keyframe2.setInterpolator(new BounceInterpolator());

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", keyframe0, keyframe1, keyframe2);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

这里给最后一帧添加回弹插值器(BounceInterpolator)



从效果图中可以看出,keyframe1到keyframe2中间值计算过程中使用了回弹插值器。所以,也可以验证上面的论述,如果给当前帧添加插值器,在上一帧到当前值的中间值计算过程中会使用这个插值器。

Keyframe之ofObject()

与ofInt,ofFloat一样,ofObject也有两个构造函数

public static Keyframe ofObject(float fraction)
public static Keyframe ofObject(float fraction, Object value)

同样的是,如果使用 ofObject(float fraction)函数来构造,也必须使用setValue(Object value)函数来设置这个关键帧所对应的值。我们仍以TextView 更改字母的例子来使用Keyframe.ofObject()。

private void changeChar() {
        Keyframe keyframe0 = Keyframe.ofObject(0, 'a');
        Keyframe keyframe1 = Keyframe.ofObject(0.1f, 'm');
        Keyframe keyframe2 = Keyframe.ofObject(1f, 'z');

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("CharText", keyframe0, keyframe1, keyframe2);
        frameHolder.setEvaluator(new CharEvaluator());

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

在这个动画中,我们定义了3个帧,keyframe0表示在进度为0的时候,动画字符是 a,keyframe1表示在进度为0.1的时候,动画的字符是m,keyframe2进度结束的时候,动画的字符是z。

利用关键帧创建PropertyValuesHolder后,一定要记得设置自定义的CharEvaluator函数,再次强调,ofObject()来制作动画的收,必须调用frameHolder.setEvaluator(new CharEvaluator())函数显示设置Evaluator,因为系统根本无法知道中间值Object 真正是什么类型。

如果没有进度为0或者1时的关键帧会怎么样?

private void phoneCall3() {
        Keyframe frame0 = Keyframe.ofFloat(0, 0);
        Keyframe frame1 = Keyframe.ofFloat(0.5f, 100);
        Keyframe frame2 = Keyframe.ofFloat(1, 0);

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", frame0, frame1, frame2);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

这里有3帧,在进度0.5时,电话向右旋转100度。然后再转回来。

尝试一,去掉第0帧,以第一帧为起始位置,则结果会怎样?

 private void phoneCall3() {
//        Keyframe frame0 = Keyframe.ofFloat(0, 0);
        Keyframe frame1 = Keyframe.ofFloat(0.5f, 100);
        Keyframe frame2 = Keyframe.ofFloat(1, 0);

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", frame1, frame2);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

可以看到,动画直接从100度旋转到0度,既当没有第0帧时,动画从最近的一帧开始。


尝试二,去掉第最后一帧,则结果会怎样?

 private void phoneCall3() {
        Keyframe frame0 = Keyframe.ofFloat(0, 0);
        Keyframe frame1 = Keyframe.ofFloat(0.5f, 100);
//        Keyframe frame2 = Keyframe.ofFloat(1, 0);

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", frame0, frame1);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

很明显,如果去掉结束帧,将以最后一个关键帧为结束位置。

尝试三,只保留一个中间帧

private void phoneCall3() {
//        Keyframe frame0 = Keyframe.ofFloat(0, 0);
        Keyframe frame1 = Keyframe.ofFloat(0.5f, 100);
//        Keyframe frame2 = Keyframe.ofFloat(1, 0);

        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", frame1);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, frameHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

再单机按钮开始动画,就直接崩溃了,报错信息如下所示:

06-15 14:00:20.668 7934-7934/com.as.propertyanimator E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.as.propertyanimator, PID: 7934
    java.lang.IndexOutOfBoundsException: Invalid index 1, size is 1
        at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
        at java.util.Arrays$ArrayList.get(Arrays.java:66)
        at android.animation.FloatKeyframeSet.getFloatValue(FloatKeyframeSet.java:85)
        at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.calculateValue(PropertyValuesHolder.java:1263)
        at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1458)
        at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:978)
        at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:642)
        at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:589)
        at android.animation.ValueAnimator.start(ValueAnimator.java:1106)
        at android.animation.ValueAnimator.start(ValueAnimator.java:1117)
        at android.animation.ObjectAnimator.start(ObjectAnimator.java:852)
        at com.as.propertyanimator.AdvanceAnimatorActivity.phoneCall3(AdvanceAnimatorActivity.java:55)
        at com.as.propertyanimator.AdvanceAnimatorActivity.access$000(AdvanceAnimatorActivity.java:21)
        at com.as.propertyanimator.AdvanceAnimatorActivity$1.onClick(AdvanceAnimatorActivity.java:40)
        at android.view.View.performClick(View.java:5198)
        at android.view.View$PerformClick.run(View.java:21147)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

报错问题时数组越界,也即是说,至少要有2帧才行。

结论

示例电话响铃效果

在讲解Keyframe 的时候,我们已经完成了电话图片左右震动的效果

 Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.1f, -20);
        Keyframe keyframe2 = Keyframe.ofFloat(0.2f, 20);
        Keyframe keyframe3 = Keyframe.ofFloat(0.3f, -20);

。。。。。。 //省略

        Keyframe keyframe10 = Keyframe.ofFloat(1f, 0);

        PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofKeyframe("rotation", keyframe0, keyframe1, keyframe2,
                keyframe3, keyframe4, keyframe5, keyframe6,
                keyframe7, keyframe8, keyframe9, keyframe10);

左右震动的效果和上面的一样,就不再赘述了。然后时放大效果,再放大时,需要X轴 和 Y轴同时放大,这样才能保持图片原比例。我们需要再图片开始震动时放大1.1倍,结束后还原初始状态。放大部分的Keyframe代码如下

Keyframe scaleXFrame0 = Keyframe.ofFloat(0, 1);
        Keyframe scaleXFrame1 = Keyframe.ofFloat(0.1f, 1.1f);
        Keyframe scaleXFrame2 = Keyframe.ofFloat(0.9f, 1.1f);
        Keyframe scaleXFrame3 = Keyframe.ofFloat(1f, 1f);

        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe("scaleX", scaleXFrame0, scaleXFrame1, scaleXFrame2, scaleXFrame3);

        Keyframe scaleYFrame0 = Keyframe.ofFloat(0, 1);
        Keyframe scaleYFrame1 = Keyframe.ofFloat(0.1f, 1.1f);
        Keyframe scaleYFrame2 = Keyframe.ofFloat(0.9f, 1.1f);
        Keyframe scaleYFrame3 = Keyframe.ofFloat(1, 1);

        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe("scaleY", scaleYFrame0, scaleYFrame1, scaleYFrame2, scaleYFrame3);

最后开始动画,代码如下

 ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, rotationHolder, scaleX, scaleY);
        objectAnimator.setDuration(2000);
        objectAnimator.start();

下面贴出完整代码

/**
 * 属性动画进阶
 */
public class AdvanceAnimatorActivity extends AppCompatActivity {

    private TextView tv1;
    private TextView tv2;
    private AppCompatImageView iv1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_advance_animator);

        AppCompatButton btnStart = findViewById(R.id.btnStart);
        tv1 = findViewById(R.id.tv1);
        tv2 = findViewById(R.id.tv2);
        iv1 = findViewById(R.id.iv1);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                phoneCall4();
            }
        });
    }

    private void phoneCall4() {
        Keyframe keyframe0 = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.1f, -20);
        Keyframe keyframe2 = Keyframe.ofFloat(0.2f, 20);
        Keyframe keyframe3 = Keyframe.ofFloat(0.3f, -20);
        Keyframe keyframe4 = Keyframe.ofFloat(0.4f, 20);
        Keyframe keyframe5 = Keyframe.ofFloat(0.5f, -20);
        Keyframe keyframe6 = Keyframe.ofFloat(0.6f, 20);
        Keyframe keyframe7 = Keyframe.ofFloat(0.7f, -20);
        Keyframe keyframe8 = Keyframe.ofFloat(0.8f, 20);
        Keyframe keyframe9 = Keyframe.ofFloat(0.9f, -20);
        Keyframe keyframe10 = Keyframe.ofFloat(1f, 0);

        PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofKeyframe("rotation", keyframe0, keyframe1, keyframe2,
                keyframe3, keyframe4, keyframe5, keyframe6,
                keyframe7, keyframe8, keyframe9, keyframe10);


        Keyframe scaleXFrame0 = Keyframe.ofFloat(0, 1);
        Keyframe scaleXFrame1 = Keyframe.ofFloat(0.1f, 1.1f);
        Keyframe scaleXFrame2 = Keyframe.ofFloat(0.9f, 1.1f);
        Keyframe scaleXFrame3 = Keyframe.ofFloat(1f, 1f);

        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe("scaleX", scaleXFrame0, scaleXFrame1, scaleXFrame2, scaleXFrame3);

        Keyframe scaleYFrame0 = Keyframe.ofFloat(0, 1);
        Keyframe scaleYFrame1 = Keyframe.ofFloat(0.1f, 1.1f);
        Keyframe scaleYFrame2 = Keyframe.ofFloat(0.9f, 1.1f);
        Keyframe scaleYFrame3 = Keyframe.ofFloat(1, 1);

        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe("scaleY", scaleYFrame0, scaleYFrame1, scaleYFrame2, scaleYFrame3);

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv1, rotationHolder, scaleX, scaleY);
        objectAnimator.setDuration(2000);
        objectAnimator.start();

    }
}

这个示例到这里就结束了,从中可以看出,借助Keyframe,不需要使用AnimatorSet,也能实现多个动画同时播放。这也是ObjectAnimator 中唯一一个能实现多个动画同时播放的方法。其它的ObjectAnimator.ofInt(),ObjectAnimator.ofFloat(),ObjectAnimator.ofObject()函数只能实现针对一个属性动画操作。

上一篇 下一篇

猜你喜欢

热点阅读