/**
  * Constructs and returns a PropertyValuesHolder object with the specified property name and set
  * of values. These values can be of any type, but the type should be consistent so that an
  * appropriate {@link android.animation.TypeEvaluator} can be found that matches the common type.
  *
  * <p>If there is only one value, it is assumed to be the end value of an animation, and an
  * initial value will be derived, if possible, by calling a getter function on the object. Also,
  * if any value is null, the value will be filled in when the animation starts in the same way.
  * This mechanism of automatically getting null values only works if the PropertyValuesHolder
  * object is used in conjunction {@link ObjectAnimator}, and with a getter function derived
  * automatically from <code>propertyName</code>, since otherwise PropertyValuesHolder has no way
  * of determining what the value should be.
  *
  * @param propertyName The name of the property associated with this set of values. This can be
  *     the actual property name to be used when using a ObjectAnimator object, or just a name used
  *     to get animated values, such as if this object is used with an ValueAnimator object.
  * @param values The set of values to animate between.
  */
 public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
   KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
   if (keyframeSet instanceof IntKeyframeSet) {
     return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
   } else if (keyframeSet instanceof FloatKeyframeSet) {
     return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
   } else {
     PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
     pvh.mKeyframeSet = keyframeSet;
     pvh.mValueType = values[0].getType();
     return pvh;
   }
 }
 @Override
 public PropertyValuesHolder clone() {
   try {
     PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
     newPVH.mPropertyName = mPropertyName;
     // newPVH.mProperty = mProperty;
     newPVH.mKeyframeSet = mKeyframeSet.clone();
     newPVH.mEvaluator = mEvaluator;
     return newPVH;
   } catch (CloneNotSupportedException e) {
     // won't reach here
     return null;
   }
 }
 /**
  * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used to
  * calculate animated values.
  */
 void init() {
   if (mEvaluator == null) {
     // We already handle int and float automatically, but not their Object
     // equivalents
     mEvaluator =
         (mValueType == Integer.class)
             ? sIntEvaluator
             : (mValueType == Float.class) ? sFloatEvaluator : null;
   }
   if (mEvaluator != null) {
     // KeyframeSet knows how to evaluate the common types - only give it a custom
     // evaluator if one has been set on this class
     mKeyframeSet.setEvaluator(mEvaluator);
   }
 }
 @Override
 public String toString() {
   return mPropertyName + ": " + mKeyframeSet.toString();
 }
 /**
  * Function used to calculate the value according to the evaluator set up for this
  * PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
  *
  * @param fraction The elapsed, interpolated fraction of the animation.
  */
 void calculateValue(float fraction) {
   mAnimatedValue = mKeyframeSet.getValue(fraction);
 }
 /**
  * The TypeEvaluator will the automatically determined based on the type of values supplied to
  * PropertyValuesHolder. The evaluator can be manually set, however, if so desired. This may be
  * important in cases where either the type of the values supplied do not match the way that they
  * should be interpolated between, or if the values are of a custom type or one not currently
  * understood by the animation system. Currently, only values of type float and int (and their
  * Object equivalents: Float and Integer) are correctly interpolated; all other types require
  * setting a TypeEvaluator.
  *
  * @param evaluator
  */
 public void setEvaluator(TypeEvaluator evaluator) {
   mEvaluator = evaluator;
   mKeyframeSet.setEvaluator(evaluator);
 }
 /**
  * Set the animated values for this object to this set of Objects. If there is only one value, it
  * is assumed to be the end value of an animation, and an initial value will be derived, if
  * possible, by calling a getter function on the object. Also, if any value is null, the value
  * will be filled in when the animation starts in the same way. This mechanism of automatically
  * getting null values only works if the PropertyValuesHolder object is used in conjunction {@link
  * ObjectAnimator}, and with a getter function derived automatically from <code>propertyName
  * </code>, since otherwise PropertyValuesHolder has no way of determining what the value should
  * be.
  *
  * @param values One or more values that the animation will animate between.
  */
 public void setObjectValues(Object... values) {
   mValueType = values[0].getClass();
   mKeyframeSet = KeyframeSet.ofObject(values);
 }
 /**
  * Set the animated values for this object to this set of floats. If there is only one value, it
  * is assumed to be the end value of an animation, and an initial value will be derived, if
  * possible, by calling a getter function on the object. Also, if any value is null, the value
  * will be filled in when the animation starts in the same way. This mechanism of automatically
  * getting null values only works if the PropertyValuesHolder object is used in conjunction {@link
  * ObjectAnimator}, and with a getter function derived automatically from <code>propertyName
  * </code>, since otherwise PropertyValuesHolder has no way of determining what the value should
  * be.
  *
  * @param values One or more values that the animation will animate between.
  */
 public void setFloatValues(float... values) {
   mValueType = float.class;
   mKeyframeSet = KeyframeSet.ofFloat(values);
 }
 /**
  * Set the animated values for this object to this set of ints. If there is only one value, it is
  * assumed to be the end value of an animation, and an initial value will be derived, if possible,
  * by calling a getter function on the object. Also, if any value is null, the value will be
  * filled in when the animation starts in the same way. This mechanism of automatically getting
  * null values only works if the PropertyValuesHolder object is used in conjunction {@link
  * ObjectAnimator}, and with a getter function derived automatically from <code>propertyName
  * </code>, since otherwise PropertyValuesHolder has no way of determining what the value should
  * be.
  *
  * @param values One or more values that the animation will animate between.
  */
 public void setIntValues(int... values) {
   mValueType = int.class;
   mKeyframeSet = KeyframeSet.ofInt(values);
 }