一. 原理 实现动画的原理:直接对对象的属性值进行改变操作,从而实现动画效果 
(注意:ObjectAnimator类继承自ValueAnimator类,即底层的动画实现机制是基于ValueAnimator类)
二. 类似ValueAnimator的使用形式 1.Java代码的使用方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ObjectAnimator ofInt (Object target, String propertyName, int ... values)   ;ObjectAnimator ofFloat (Object target, String propertyName, float ... values)  ;ObjectAnimator ofArgb (Object target, String propertyName, int ... values)  ObjectAnimator ofObject (Object target, String propertyName,              TypeEvaluator evaluator, Object... values)              ObjectAnimator setDuration (long  duration)  setRepeatCount (int  value) void  setRepeatMode (@RepeatMode  int  value) void  setStartDelay (long  startDelay) void  setInterpolator (TimeInterpolator value) 
2.Xml的使用方式 ① 在路径 res/animator 的文件夹里创建动画效果.xml文件,其中animator文件夹需要自己去创建。
② 使用AnimatorInflater的public static Animator loadAnimator(Context context, @AnimatorRes int id)方法来加载写好的Xml文件动画。
③ 使用 public void setTarget(@Nullable Object target) ,设置动画对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //初始值 android:valueFrom="" //结束值 android:valueTo="" //值的类型 floatType intType 等 android:valueType="floatType" //属性名称 TranslationX ,Rotation 等 android:propertyName="" //动画持续时间 android:duration="1000" //动画重复次数 infinite是无数次 android:repeatCount="1" //动画重复模式 restart reserve android:repeatMode="restart" //设置插值器 android:interpolator="" 
1 2 3 4 5 6 7 8 ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(this ,R.animator.animation); animator.setTarget(view); animator.start(); 
三. 类似补间动画的 平移 缩放 旋转 透明度 1. 平移 ① 运行效果
② Java 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public  class  TranslateActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_translate);                  final  ObjectAnimator translationX = ObjectAnimator.ofFloat(findViewById(R.id.view), "TranslationX" , 0f , 100f );                  translationX.setDuration(1000 );                  translationX.setRepeatCount(1 );                  translationX.setRepeatMode(ValueAnimator.RESTART);                  final  ObjectAnimator translationY = ObjectAnimator.ofFloat(findViewById(R.id.view), "TranslationY" , 0f , 100f );                  translationY.setDuration(1000 );                  translationY.setRepeatCount(1 );                  translationY.setRepeatMode(ValueAnimator.RESTART);         findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {             @Override              public  void  onClick (View v)                                    translationX.start();                 translationY.start();             }         });     } } 
③ Xml方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="utf-8"?> <objectAnimator  xmlns:android ="http://schemas.android.com/apk/res/android"      android:valueFrom ="0"      android:valueTo ="100"      android:valueType ="floatType"      android:propertyName ="TranslationX"      android:duration ="1000"      android:repeatCount ="1"      android:repeatMode ="restart" />         <?xml version="1.0" encoding="utf-8"?>  <objectAnimator  xmlns:android ="http://schemas.android.com/apk/res/android"      android:valueFrom ="0"      android:valueTo ="100"      android:valueType ="floatType"      android:propertyName ="TranslationY"      android:duration ="1000"      android:repeatCount ="1"      android:repeatMode ="restart" /> 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public  class  TranslateActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_translate);                  View view = findViewById(R.id.view);                  final  ObjectAnimator translateX = (ObjectAnimator) AnimatorInflater.loadAnimator(this , R.animator.translatex_animator);         final  ObjectAnimator translateY = (ObjectAnimator) AnimatorInflater.loadAnimator(this , R.animator.translatey_animator);                  translateX.setTarget(view);         translateY.setTarget(view);                  findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {             @Override              public  void  onClick (View v)                   translateX.start();                 translateY.start();             }         });     } } 
2. 缩放 ① 运行效果
② Java 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public  class  ScaleActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_scale);                  final  View view = findViewById(R.id.view);                  view.post(new  Runnable() {             @Override              public  void  run ()                                    view.setPivotX(view.getWidth());                 view.setPivotY(view.getHeight());                                  final  ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "ScaleX" , 1.0f , 1.5f );                                  scaleX.setDuration(1000 );                                  scaleX.setRepeatCount(1 );                                  scaleX.setRepeatMode(ValueAnimator.RESTART);                                  final  ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "ScaleY" , 1.0f , 1.5f );                                  scaleY.setDuration(1000 );                                  scaleY.setRepeatCount(1 );                                  scaleY.setRepeatMode(ValueAnimator.RESTART);                                  findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {                     @Override                      public  void  onClick (View v)                           scaleX.start();                         scaleY.start();                     }                 });             }         });     } } 
③ Xml方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="utf-8"?> <objectAnimator  xmlns:android ="http://schemas.android.com/apk/res/android"      android:valueFrom ="1.0"      android:valueTo ="1.5"      android:valueType ="floatType"      android:propertyName ="ScaleX"      android:duration ="1000"      android:repeatCount ="1"      android:repeatMode ="restart" />   <?xml version="1.0" encoding="utf-8"?> <objectAnimator  xmlns:android ="http://schemas.android.com/apk/res/android"      android:valueFrom ="1.0"      android:valueTo ="1.5"      android:valueType ="floatType"      android:propertyName ="ScaleY"      android:duration ="1000"      android:repeatCount ="1"      android:repeatMode ="restart" /> 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  class  ScaleActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_scale);                  final  View view = findViewById(R.id.view);                  final  ObjectAnimator scaleX = (ObjectAnimator) AnimatorInflater.loadAnimator(this ,R.animator.scalex_animator);         scaleX.setTarget(view);                  final  ObjectAnimator scaleY = (ObjectAnimator) AnimatorInflater.loadAnimator(this ,R.animator.scaley_animator);         scaleY.setTarget(view);                  view.post(new  Runnable() {             @Override              public  void  run ()                                    view.setPivotX(view.getWidth());                 view.setPivotY(view.getHeight());                                  findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {                     @Override                      public  void  onClick (View v)                           scaleX.start();                         scaleY.start();                     }                 });             }         });     } } 
3. 旋转 ① 运行效果
② Java 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public  class  RotateActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_rotate);                  final  View view = findViewById(R.id.view);                  view.post(new  Runnable() {             @Override              public  void  run ()                                    view.setPivotX(view.getWidth());                 view.setPivotY(view.getHeight());                                  final  ObjectAnimator rotation = ObjectAnimator.ofFloat(view, "Rotation" , 0.0f , 90.0f );                                  rotation.setDuration(1000 );                                  rotation.setRepeatCount(1 );                                  rotation.setRepeatMode(ValueAnimator.RESTART);                                  findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {                     @Override                      public  void  onClick (View v)                           rotation.start();                     }                 });             }         });     } } 
③ Xml方式
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="utf-8"?> <objectAnimator  xmlns:android ="http://schemas.android.com/apk/res/android"      android:valueFrom ="0"      android:valueTo ="90"      android:valueType ="floatType"      android:propertyName ="Rotation"      android:duration ="1000"      android:repeatCount ="1"      android:repeatMode ="restart"      /> 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public  class  RotateActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_rotate);                  final  View view = findViewById(R.id.view);                  final  ObjectAnimator rotate = (ObjectAnimator) AnimatorInflater.loadAnimator(this ,R.animator.rotate_animation);         rotate.setTarget(view);                  view.post(new  Runnable() {             @Override              public  void  run ()                                    view.setPivotX(view.getWidth());                 view.setPivotY(view.getHeight());                                  findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {                     @Override                      public  void  onClick (View v)                           rotate.start();                     }                 });             }         });     } } 
4. 透明度 ① 运行效果
② Java 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  class  AlphaActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_alpha);                  final  ObjectAnimator alpha = ObjectAnimator.ofFloat(findViewById(R.id.view), "Alpha" , 1.0f , 0.5f );                  alpha.setDuration(1000 );                  alpha.setRepeatCount(1 );                  alpha.setRepeatMode(ValueAnimator.RESTART);         findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {             @Override              public  void  onClick (View v)                                    alpha.start();             }         });     } } 
③ Xml方式
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="utf-8"?> <objectAnimator  xmlns:android ="http://schemas.android.com/apk/res/android"      android:valueFrom ="0"      android:valueTo ="90"      android:valueType ="floatType"      android:propertyName ="Rotation"      android:duration ="1000"      android:repeatCount ="1"      android:repeatMode ="restart"      /> 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  class  AlphaActivity  extends  AppCompatActivity      @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_alpha);                  View view = findViewById(R.id.view);                  final  ObjectAnimator alpha = (ObjectAnimator) AnimatorInflater.loadAnimator(this , R.animator.alpha_animator);                  alpha.setTarget(view);                  findViewById(R.id.start).setOnClickListener(new  View.OnClickListener() {             @Override              public  void  onClick (View v)                   alpha.start();             }         });     } } 
四. ObjectAnimator的实现原理 1. String property 前面的例子中,我们给String property传入alpha、rotation、translationX 和 scaleY 等,还可以传入什么呢?
答案是任意属性值。
①ObjectAnimator类,通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果。而自动赋给对象的属性的本质是调用该对象属性的 set() & get() 方法进行赋值。
第二个参数传入值的作用是:让ObjectAnimator类 根据传入的属性名去寻找该对象对应属性名的 set() & get() 方法,从而进行对象属性值的赋值。
2. 自动赋值的逻辑 ① 初始化时,如果属性的初始值没有提供,则调用属性的 get() 进行取值;
② 当值变化时,用对象该属性的 set() 方法,从而从而将新的属性值设置给对象属性。
3. 总结 ① ObjectAnimator 类针对的是任意对象 & 任意属性值,并不是单单针对于View对象。
② 如果需要采用ObjectAnimator 类实现动画效果,那么需要操作的对象就必须有该属性的 set() & get()。
五. 实例使用 1. 实现步骤 对于属性动画,其拓展性在于:不局限于系统限定的动画,可以自定义动画,即自定义对象的属性,并通过操作自定义的属性从而实现动画。
步骤:
① 为对象设置需要操作属性的 set() & get() 方法。
② 通过实现TypeEvaluator类从而定义属性变化的逻辑。
③ 使用 ObjectAnimator 的 ofObject() 创建动画。
2. 第一种方式添加 set() & get() 方法 通过继承原始类,直接给类加上该属性的 get() & set(),从而实现给对象加上该属性的 get() & set() 。
① 运行效果
② 代码
自定义的估值器 ColorEvaluator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 public  class  ColorEvaluator  implements  TypeEvaluator                private  int  currentRed;     private  int  currentGreen;     private  int  currentBlue;     @Override      public  Object evaluate (float  fraction, Object startValue, Object endValue)                    String startColor = (String) startValue;         String endColor = (String) endValue;                  int  startRed = Integer.parseInt(startColor.substring(1 ,3 ),16 );         int  startGreen = Integer.parseInt(startColor.substring(3 ,5 ),16 );         int  startBlue = Integer.parseInt(startColor.substring(5 ,7 ),16 );                  int  endRed = Integer.parseInt(endColor.substring(1 ,3 ),16 );         int  endGreen = Integer.parseInt(endColor.substring(3 ,5 ),16 );         int  endBlue = Integer.parseInt(endColor.substring(5 ,7 ),16 );                  int  redDiff = Math.abs(startRed-endRed);         int  greenDiff = Math.abs(startGreen-endGreen);         int  blueDiff = Math.abs(startBlue-endBlue);         int  colorDiff = redDiff + greenDiff + blueDiff;                  if  (currentRed != endRed){             currentRed = getCurrentColor(startRed,endRed,colorDiff,0 ,fraction);         }         if  (currentGreen != endGreen){             currentGreen = getCurrentColor(startGreen,endGreen,colorDiff,redDiff,fraction);         }         if  (currentBlue != endBlue){             currentBlue = getCurrentColor(startBlue,endBlue,colorDiff,redDiff+greenDiff,fraction);         }                  return  "#"  + getHexString(currentRed) + getHexString(currentGreen) + getHexString(currentBlue);     }          public  int  getCurrentColor (int  startColor,int  endColor,int  colorDiff,int  offset,float  fraction)                   int  currentColor;                  if  (startColor >= endColor){             currentColor = (int ) (startColor - (fraction * colorDiff - offset));         }else  {             currentColor = (int ) (startColor + (fraction * colorDiff - offset));         }         return  currentColor;     }          private  String getHexString (int  value)                   String hexString = Integer.toHexString(value);                  if  (hexString.length() == 1 ){             hexString = "0"  + hexString;         }         return  hexString;     } } 
自定义的View MyView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public  class  MyView  extends  View           public  static  final  float  RADIUS = 100f ;          private  Paint paint;          private  String color;          public  MyView (Context context, @Nullable  AttributeSet attrs)           super (context, attrs);                  paint = new  Paint(Paint.ANTI_ALIAS_FLAG);         paint.setColor(Color.BLUE);     }          @Override      protected  void  onDraw (Canvas canvas)           canvas.drawCircle(500 ,500 ,RADIUS,paint);     }          public  String getColor ()           return  color;     }     public  void  setColor (String color)           this .color = color;                  paint.setColor(Color.parseColor(color));                  invalidate();     } } 
通过对 自定义的 MyView 中的 Color 属性操作,每次自动赋值后都会刷新界面重绘一个圆。
这就是通过继承原始类,直接给类加上该属性的 get() & set()。
2. 第二种方式添加 set() & get() 方法 通过包装原始动画对象,间接给对象加上该属性的 get()&
从原理上说,如果想让对象的属性a的动画生效,属性a需要同时满足下面两个条件:
不满足第二条的例子:由于View的setWidth()并不是设置View的宽度,而是设置View的最大宽度和最小宽度的;所以通过setWidth()无法改变控件的宽度;所以对View视图的width做属性动画没有效果。
① 运效果
② 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package  swu.xl.property_object_example_two;import  androidx.appcompat.app.AppCompatActivity;import  android.animation.ObjectAnimator;import  android.os.Bundle;import  android.view.View;import  android.view.ViewGroup;import  android.widget.Button;public  class  MainActivity  extends  AppCompatActivity     @Override      protected  void  onCreate (Bundle savedInstanceState)           super .onCreate(savedInstanceState);         setContentView(R.layout.activity_main);                  View view = findViewById(R.id.btn);                  final  ViewWrapper viewWrapper = new  ViewWrapper(view);                  view.setOnClickListener(new  View.OnClickListener() {             @Override              public  void  onClick (View v)                   ObjectAnimator.ofInt(viewWrapper,"width" ,300 ,600 ).setDuration(3000 ).start();             }         });    }          private  static  class  ViewWrapper         private  View target;                public  ViewWrapper (View target)             this .target = target;        }                public  void  setWidth (int  width)                         ViewGroup.LayoutParams layoutParams = target.getLayoutParams();                        layoutParams.width = width;                        target.setLayoutParams(layoutParams);        }        public  int  getWidth ()             return  target.getLayoutParams().width;        }     } } 
通过包装原始动画对象完成View的Width属性动画。本质上是采用了设计模式中的装饰模式,即通过包装类从而扩展对象的功能。
参考文章 Android ObjectAnimator类学习指南:手把手带你学会如何自定义属性动画