要散布阳光到别人心里,先得自己心里有阳光。——罗曼·罗兰
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()函数只能实现针对一个属性动画操作。
网友评论