Android Animation
Android 动画
标签(空格分隔): android animation
动画基础
Android 基础动画分为 alpha(透明度)、scale(伸缩)、translate(位移)、rotate(旋转)
需要注意:的是,View 中包含了基础动画的相关操作,所以一般的 View 控件都能够进行简单的动画操作(View Animation)。button.setScaleX(伸缩量)
动画的分类
1.View Animation -- (补间动画 Tween Animation & 帧动画 Frame Animation)
2.Property Animation -- (属性动画 ValueAnimator & ObjectAnimator)
View动画的实现方式
- 通过 xml 文件定义动画;
- 通过创建相应的动画对象对动画进行设置;
xml 文件实现方式
xml 文件中设置动画的效果的属性格式是 from* ,to* 从某一个数字变化到另一个数值,Studio 中会有提示,另外会有没提示的属性如下
android:duration="time" 设置动画的持续时间以毫秒为单位
android:fillAfter="T/F" 设置是否保持动画结束状态
android:fillBefore="T/F" 设置是否还原动画初始状态 (与 fillEnable 的效果一致)
android:repeatMode="restart/reverse" 设置回放类型 重新开始/反转
此外还需要注意:一个属性:android:pivotX="" / android:pivotY=""
这个属性表示缩放起点 X/Y 轴坐标(数值、百分比、百分比p 三种样式)
· 为数值(50)时,表示在当前 view 的左上角,在对应方向加上 50 作为起始点
· 为百分比(50%)时,表示在当前控件的左上角,在对应方向加上自己宽度的 50% 作为起始点
· 为百分比p(50%p)时,表示在当前控件的左上角,在对应方向加上父控件宽度的 50% 作为起始点
若希望能够使用组合动画,则需要定义 <set> 标签,设置 duration,再包含所需要的动画标签就可以实现简单的组合动画
<!-- 简单组合动画实现 -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
<scale
android:fromXScale="0.0"
android:toXScale="1.4"
android:fromYScale="0.0"
android:toYScale="1.4"
android:pivotX="50%"
android:pivotY="50%"/>
<!-- 0~360表示旋转一圈 -->
<rotate
android:fromDegrees="0"
android:toDegrees="720"
android:pivotX="50%"
android:pivotY="50%"/>
</set>
动画的 xml 文件需要存放在 res/anim 文件夹下,调用方式(以 ScaleAnimation 为例)
ScaleAniamtion scaleAnimation = Animation.loadAnimation(context,R.anim.animationName);
view.startAnimation(scaleAnimation);
动画可以指定 Interpolator 属性来控制动画的变化过程(普通动画都继承了这个属性)
AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后然后向前甩
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 在动画开始的地方快然后慢
LinearInterpolator 以常量速率改变
OvershootInterpolator 向前甩一定值后再回到原来位置
代码形式动态生成动画以及 Interpolator
四种动画都有对应的 Animation 类均继承自 Animation,动画的创建均体现在创建对象的时候,形参包含 context 的构造方法基本不用,因为可以进行自定义动画。
此外需要注意: AnimationSet 这个类,构造是需要指定是否定义一个 Iterpolator 供 Set 下的动画共用,若为 false 那么动画可以定义各自的插值器。
// 这里以 AnimationSet 为例实现上面 xml 的效果
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
ScaleAnimation scaleAnimation = new ScaleAnimation(0,1.4f,0,1.4f, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
RotateAnimation rotateAnimation = new RotateAnimation(0,720f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.setDuration(3000);
animationSet.setFillAfter(true);
// 使用 Interpolator 上面提到的 Interpolator 可以直接创建起对象进行调用就有对应的效果
animationSet.setInterpolator(new AccelerateDecelerateInterpolator());
属性动画出现原因:
1.Property Animator 能够实现View Animation无法实现的功能
2.View Animation 仅能对指定控件做动画而 Property Animator 是用过改变控件某一属性来做动画的
(
View Animation 可以对控件做动画,但实际的属性信息如位置、颜色等改变不了
Property Animation 就是改变控件的属性达到动画的效果如位置、颜色
)
ValueAnimator
概述:这个类不会对控件做任何操作,是通过监听值的渐变过程来操作控件,所以需要监听 ValueAnimator 的动画过程来对控件做操作
// 使用例子
ValueAnimator animator = ValueAnimator.ofInt(0,400);
// 设置动画的重复模式
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
// 设置动画的重复次数 INFINITE 是无限循环
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
animator.setDuration(1000);
// 指定弹跳插值器
valueAnimator.setInterpolator(new BounceInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//TODO 获取 animation 中值的变化放到控件中
int value = (int)animation.getAnimatedValue();
// 改变控件的位置(左、上、右、下)
tvShow.layout(tvShow.getLeft(),value,tvShow.getRight(),value+tvShow.getHeight());
}
});
animator.start();
获取 ValueAnimator 的对象有 ofInt() ofFloat(),参数是一个可变长参数列表,比如传入 param1,param2,param3 那么值的变化就是从 param1 -> param2 -> param3
cancel()
-- 用于停止动画的播放并将往后的动画取消掉
ValueAnimator 的监听器:addUpdateListener(AnimatorUpdateListener) & addListener(AnimatorListener)
AnimatorUpdateListener
-- 监听动画的变化过程,在回调里可以获取动画在整个过程中的值;AnimatorListener
-- 主要监听 start、end、cancel、repeat 四个状态;
通过上面的两个方法可以为 ValueAnimator 添加监听器,移除监听器可以通过以下方法:removeListener(AnimatorListener listener) / removeAllListeners()
removeUpdateListener(AnimatorUpdateListener listener) / removeAllUpdateListeners()
前者用于移除指定监听器,后者将所有的监听器都移除。若将监听器移除,监听器中的操作将不会执行,需要区分取消监听器和取消动画的区别,若取消的监听器中没有包含对动画状态的操作,那么动画还是会继续执行只不过没有了对这个动画的监听而已。
// ValueAnimator 其他函数
/**
* 延时多长时间开始,单位:毫秒
*/
public void setStartDelay(long startDelay)
/**
* 完全克隆一个 ValueAnimator 实例,包括它所有的设置以及所有对监听器代码的处理
*/
public ValueAnimator clone()
自定义插值器
所有已有的 Interpolator 都是实现了 TimeInterpolator 这个接口,而这个接口中有一个方法float getInterpolation(float input);
参数 input 取值范围 0 到 1,表示当前动画的进度
· 取 0 时表示动画开始
· 取 1 时表示动画结束
· 取 0.5 时表示动画中间的位置
返回值表示当前实际想要显示的进度,取值可以超过 1 也可以小于 0
· 超过 1 表示已经超过目标值
· 小于 0 表示小于开始位置
注意:这个方法的形参跟程序员自身设定的值没有一点关系,这个参数就是表示动画整个执行过程的执行时间。
/**
* 简单实现自定义一个插值器实现跟 LinearInterpolator 相反的变化过程
*/
public class SelfDefineInterpolator implements TimeInterpolator{
@Override
public float getInterpolation(float input) {
return 1-input;
}
}
Evaluator(转换器)
作用:将 Interpolator 返回的百分值转换为具体的数值提供给监听器进行调用
过程:
ofInt(0,400) -> Interpolator -> Eva
注意: Evaluator 对应的数值类型是专用的(使用 int 进行运算那么返回也是 int)
/* 调用转换器 ofInt() 的前提就调用 IntEvaluator,ofInt 跟 ofFloat 都会默认调用对应的 Evaluator,此外还有一个颜色变化 -- (ArgbEvaluator)*/
animator.setEvaluator(new IntEvaluator());
/**
* 自定义 Evaluator,在原本的 IntEvaluator 基础上加了 200 的起始值
* 即传入(0,400)区间的话在这个转换器中的实际区间为(200,600)
*/
public class MyEvaluator implements TypeEvaluator<Integer>{
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(200 + startInt + fraction * (endValue - startInt));
}
}
因此可以通过重写 Interpolator 改变数值进度来改变数值位置,也可以通过改变 Evaluator 中进度所对应的数值来改变数值位置,在 Interpolator 中的修改迁移到 Evaluator 的效果是一样的。
简述 AgrbEvaluator (a -- alpha;r -- red;g -- green;b -- blue)
这个类的 evaluator() 返回 int 类型的数据就是说可以使用 ofInt() 进行调用该转换器;
传参需要传入十六进制的颜色值(0xffffffff);ofObject() 概述
由于 ofInt() 和 ofFloat() 这两个函数分别只能接受 int 类型和 float 类型的值,如果想要使用如字符串的变换时这两个方法就不能够满足需求了,因此出现这个方法。
/** 源码,ofObject() 的实现就是创建一个 ValueAnimator 对象并对其进行初始化赋值
* 反观 ofInt 和 ofFloat 都是将 values 传递给 ValueAnimator 对象然后返回
* 只不过 ofObject 这个方法多了一个求值程序(evaluator)
*/
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values){
ValueAnimator anim = new ValueAnimator();
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
ofObject() 这个方法需要指定求值程序是为了方便开发者在传入自定义对象进行动画改变的时候也自定义一个对应的求值程序(Evaluator),以便系统的执行,因为系统提供的 Evaluator 不一定能满足自定义对象值的变化。
这个自定义的方法通常与自定义 view 结合使用,基本步骤如下:
/**
* · 自定义用于承载变化数值的类
* · 自定义用于产生动画变化的 view
* · 画出该 view
* · 自定义 Evaluator
* · 实现动画
* 调用步骤:使用自定义的 view 调用动画的方法即可
* /
// 代码实现
// 实体类
public class Point {
private int radius;
public Point(int radius) {
this.radius = radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getRadius() {
return radius;
}
}
// 自定义 view
public class MyPointView extends View {
private Point mCurPoint;
public MyPointView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mCurPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);
}
}
public void doPointAnim(){
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.start();
}
}
// 求值类
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int start = startValue.getRadius();
int end = endValue.getRadius();
int curValue = (int) (start + fraction * (end - start));
return new Point(curValue);
}
}
ObjectAnimator 使用
由于 ValueAnimator 只是针对属性值的变化不能修改整个控件的属性因此派生出 ObjectAnimator 因此 ValueAnimator 的方法 ObjectAnimator 都有。
一般用法就是在构建 ObjectAnimator 的时候同时指定需要执行动画的控件和执行的动画。
// 源码,values 表示变化过程,源码中将 values 的数据添加到 ObjectAnimator 中
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
// 例子:透明度从 1 到 0 再到 1
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"alpha",1,0,1);
objectAnimator.setDuration(1000);
objectAnimator.start();
流程
ofFloat(**) -> 加速器 -> Evaluator -> 调用 set 函数
最后一步调用 set 函数是根据属性拼装 set 函数后反射调用,并将当前值作为参数传入(set 函数拼装的意思就是将属性的首字母大写然后拼接在 set 之后就成了设置某个属性值的函数,拼装成之后在通过反射调用该函数)
注意:ObjectAnimator 只负责把当前运动动画的数值传给 set 函数,至于 set 函数里面怎么做可以自行定义
// 以上面的 MyPointView 为例实现一个 ObjectAnimator
// MyPointView 中添加/修改以下代码
private Point mObjPoint = new Point(100);
@Override
protected void onDraw(Canvas canvas) {
if(mObjPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mObjPoint.getRadius(),paint);
}
super.onDraw(canvas);
}
/**
* 设置半径,供反射调用
* @param radius
*/
void setPointRadius(int radius){
mObjPoint.setRadius(radius);
invalidate();
}
// 实际使用
private void doViewPointAnim() {
// 这个 propertyName 就是自己定义在 MyPointView 定义的 setPointRadius() set 后面的属性
objectAnimator = ObjectAnimator.ofInt(myPointView,"pointRadius",0,300,100);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.start();
}
ObjectAnimator 会通过反射调用对应 view 的 setPropertyName() 方法,从一个完整的 bean 而言必然会有一个 getPropertyName() 方法,那么什么时候 ObjectAnimator 会调用这个 get 方法呢?就是当开发者在实例化 ObjectAnimator 对象的时候传入一个属性值的时候(通常 ObjectAnimator 是使用一组数据来进行数据的变化)因此当传入一个属性值的时候它会提示警告没有初值所以默认会使用定义的类型的默认值(比如 int 值的默认值是 0),那使用 ofInt() 实例化对象时仅指定了一个值那么就会使用默认值作为变化的起始值。若想要改变默认初始值的话可以添加 getPropertyName() 自定义初始值,如本例
/**
* 设置初始值,供反射调用
* @return
*/
public int getPointRadius(){
return 50;
}
上述提到的动画操作中包含了动画的基本操作,但是我们发现每一个 Animator 无论是 Value 还是 Object 的对象显示单一的动画比如旋转、渐变等效果,那么就会有疑问说能不能在代码中将动画整合起来然后一起显示呢?(就像 xml 中设置 set 动画集一样)答案是肯定的。
Google 应对这一需求提供了 PropertyValuesHolder 这个 API,它就保存了动画过程中所需要操作的属性和对应的值。
当我们需要组合动画的时候需要使用 ofPropertyValuesHolder 这个方法去构建 ObjectAnimator 这个类的对象(因为 ObjectAnimator 派生自 ValueAnimator 所以它也有这个方法)通过这个方法构建 ObjectAnimator 的话就需要 PropertyValuesHolder 这个类的对象,因此需要找到创建这个类的对象的方法,观察源码我们可以发现,创建这个类的对象的方法跟 ObjectAnimator 创建对象的方法名字是一致的(提供了一定的便利程度)
// PropertyValuesHolder 的创建对象的方法
public static PropertyValuesHolder ofInt(String propertyName, int... values)
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values) // 跟 Animator 一样可以引入 Evaluator
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
// 获取到 PropertyValuesHolder 的对象之后就可以 ObjectAnimator 的 ofPropertyValuesHolder 创建对象了
public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values){
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(target);
anim.setValues(values);
return anim;
}
----------------------------------源码、例子分割线-------------------------------------
/**
* 使用例子
* 返回 animator 这个对象是为了方便取消动画
*/
private ObjectAnimator rotateAndChange(){
PropertyValuesHolder rotateValuesProperty = PropertyValuesHolder.ofFloat("Rotation",60f,-60f,40f,-40f,-20f,20f,10f,-10f,0f);
PropertyValuesHolder rgbValuesProperty = PropertyValuesHolder.ofInt("BackgroundColor",0xffffffff,0xffff00ff,0xfffff00,0xffffffff);
objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tvShow,rotateValuesProperty,rgbValuesProperty);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
return objectAnimator;
}
PropertyValuesHolder 的 KeyFrame(关键帧)
一个 30 秒的动画根据一秒要播放 24 帧的图片来做的话,如果要做那么多的图片是不科学的, 因此引入关键帧的概念:一个 30 秒的动画只需要定义开始时候的关键帧和结束之后的关键帧,过程由系统去填充。
因此一个关键帧不许包含两个原素:时间点和位置 (即表示某物体在某时间点应该在哪个位置上)
/**
* Keyframe 初始化对象的方法
* fraction:表示当前的显示进度
* value:表示当前应该在的位置
*/
public static Keyframe ofFloat(float fraction, float value)
// Keyframe 的生成方式
Keyframe kf = KeyFrame.ofFloat(0,0); // 表示动画进度为 0 时,数值位置是 0(进度,位置)
使用 Keyframe 的基本步骤跟 PropertyValuesHolder 类似
· 生成 Keyframe 对象;
· 生成 PropertyValuesHolder 对象;
· 生成 ObjectAnimator 对象;
// 例子
private ObjectAnimator doKeyframeAnim(){
Keyframe keyframeStart = Keyframe.ofFloat(0,0);
Keyframe keyframeMiddle = Keyframe.ofFloat(0.1f,-20f);
Keyframe keyframeEnd = Keyframe.ofFloat(1f,0);
PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",keyframeStart,keyframeMiddle,keyframeEnd);
objectAnimator = ObjectAnimator.ofPropertyValuesHolder(tvShow,propertyValuesHolder);
objectAnimator.setDuration(1000);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
return objectAnimator;
}
需要注意的是 keyframe 设置插值器的作用是:从上一帧开始到当前设置插值器的帧时,这个过程值的计算是使用指定的插值器进行计算。不要给初始帧设置插值器因为他说第一帧,设置了也没有用。
Keyframe keyframeStart = Keyframe.ofFloat(0,0);
Keyframe keyframeMiddle = Keyframe.ofFloat(0.1f,-20f);
// 表示 start 帧到 middle 帧使用弹跳插值器
keyframeMiddle.setInterprolater(new BounceInterpolator());
Keyframe 的 ofObject 产生 keyframe 对象这个自定义的方式跟其他类的 ofObject 方法的自定义方式是一致的,定义开始时、过程中、结束时的关键帧
这三个状态的帧存在的情况:
· 去掉第 0 帧,将以第一个关键帧为起始位置
· 去掉结束帧,将以最后一个关键帧为结束位置
· 使用 keyframe 构建动画至少要有两个或两个以上的帧
AnimatorSet
之前用到的 PropertyValuesHolder 的组合动画是作用在一个控件上的,当我们有需求要进行多个控件的动画进行播放的时候就需要 AnimatorSet 这个类将,可以将各种 Animator 都放进这个 AnimatorSet 中然后进行播放,
播放有两种方式:1、playSequentially(所有动画依次播放);2、playTogether(所有动画一起播放)
两个函数的参数都有可变参数的 Animator 或者传入集合(一般使用可变参数比较方便)
(注意在使用 AnimatorSet 的时候不要与 AniamtionSet 弄混淆了,Animator 跟 Animation 是不一样的,Animation 处理的是补间动画,Animator 处理的是属性动画,AnimatorSet 设置之后会覆盖单个 ObjectAbunator)
/*
* playSequentially 例子
* 注意,这个方法的实现是调用了 play(animator-n).before(animator-n+1) 这两个发放
* 这句代码就是说在执行第 n+1 个动画之前执行第 n 个动画
* playSequentially 与 playTogether 的实现也是使用了这种链式调用
* 所以 animatorSet 也支持这种链式调用
*/
private void doPlaySequentiallyAnimator(){
ObjectAnimator objectAnimatorAlpha = ObjectAnimator.ofFloat(tvShow,"alpha",1,0,1);
ObjectAnimator objectAnimatorRotation = ObjectAnimator.ofFloat(btnStart,"rotation",0,720);
ObjectAnimator objectAnimatorScalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",1,2);
ObjectAnimator objectAnimatorRescalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",2,1);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
animatorSet.playSequentially(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4);
animatorSet.start();
}
// playTogether 例子
private void doPlayTogetherAnimator(){
ObjectAnimator objectAnimatorAlpha = ObjectAnimator.ofFloat(tvShow,"alpha",1,0,1);
ObjectAnimator objectAnimatorRotation = ObjectAnimator.ofFloat(btnStart,"rotation",0,720);
ObjectAnimator objectAnimatorScalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",1,2);
ObjectAnimator objectAnimatorRescalex = ObjectAnimator.ofFloat(btnCancel,"scaleX",2,1);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
animatorSet.playTogether(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4);
animatorSet.start();
}
注意:
1、playTogether 和 playSequentially 在**动画后,控件的动画情况与这两个方法无关,这两个方法只负责**动画;
2、playSequentially 只有上一个控件做完动画之后,才会**下一个控件的动画,若上一个控件的动画是无线循环,那下一个控件的动画将无法进行;
此外,AnimatorSet 提供延迟启动动画的功能 setStartDelay(long startDelay)
animatorSet.setStartDelay(2000);
AnimatorSet 的延时操作的注意事项
· 延时是延长 AnimatorSet **的时间,对单个动画的延时设置没有影响
· 真正**延时 = startDelay + 第一个动画.startDelay
· AnimatorSet **之后,第一个动画绝对会开始运行,后面的动画根据自己是否延时自行处理Animator 的 xml 实现
标签名
· animator -- ValueAnimator
· objectanimator -- ObjectAnimator
· set -- AnimatorSet
// 将 xml 加在到程序中
ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(context,R.animator.animatorname);
ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(context,R.animator.animatorname);
文章参考自启舰的 csdn 博客