美文网首页
Android 属性动画进阶

Android 属性动画进阶

作者: as_pixar | 来源:发表于2020-06-15 14:28 被阅读0次

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

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)
  • propertyName 表示ObjectAnimator操作的属性名,通过反射查找setPropertyName
  • values 属性所对应的参数,同样是可变参数,可以指定多个,如果只指定一个,那么ObjectAnimator会查找属性对应的getProperty()函数获取初始值。

很明显,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)
  • target 需要执行动画的控件
  • values 可以传入多个PropertyValuesHolder实例,由于每个PropertyValuesHolder实例都会针对一个属性执行动画操作,如果传入多个PropertyValuesHolder实例,则会对控件的多个属性执行动画操作。接下来我们写个例子
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)
  • propertyName 对象动画时要操作的属性名
  • evaluator 根据当前动画进度计算出当前值,可以使用系统自带的IntEvaluator,FloatEvauluator。
  • 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)
  • fraction 表示当前的显示进度,即在插值器中getInterpolation()函数的返回值。
  • valuve 动画当前所在的数值位置
    比如,Keyframe.ofFloat(0,0)表示动画进度为0时,动画所在的数值位置为0;Keyframe.ofFloat(0.25f,-500f)表示动画进度为25%时,动画所在的数值位置为-50f;
    Keyframe.ofFloat(1f,500f)表示动画结束时,动画所在的数值位置为500。

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

public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
  • propertyName 动画所要操作的属性名
  • values Keyframe 的列表,PropertyValuesHolder 会根据每个Keyframe的设定,定时将指定的值输出动画。所以,Keyframe的完整使用代码如下:
        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的构造函数所需要的参数是非常像的

  • fraction 当前关键帧的动画进度位置
  • value 当前位置所对应的值
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 来构建动画,至少要有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()函数只能实现针对一个属性动画操作。

相关文章

网友评论

      本文标题:Android 属性动画进阶

      本文链接:https://www.haomeiwen.com/subject/ryxcxktx.html