1013 lines
44 KiB
Java
1013 lines
44 KiB
Java
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.actionbarsherlock.internal.nineoldandroids.animation;
|
|
|
|
//import android.util.FloatProperty;
|
|
//import android.util.IntProperty;
|
|
import android.util.Log;
|
|
//import android.util.Property;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
|
|
/**
|
|
* This class holds information about a property and the values that that property
|
|
* should take on during an animation. PropertyValuesHolder objects can be used to create
|
|
* animations with ValueAnimator or ObjectAnimator that operate on several different properties
|
|
* in parallel.
|
|
*/
|
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
public class PropertyValuesHolder implements Cloneable {
|
|
|
|
/**
|
|
* The name of the property associated with the values. This need not be a real property,
|
|
* unless this object is being used with ObjectAnimator. But this is the name by which
|
|
* aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
|
|
*/
|
|
String mPropertyName;
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
//protected Property mProperty;
|
|
|
|
/**
|
|
* The setter function, if needed. ObjectAnimator hands off this functionality to
|
|
* PropertyValuesHolder, since it holds all of the per-property information. This
|
|
* property is automatically
|
|
* derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
|
|
*/
|
|
Method mSetter = null;
|
|
|
|
/**
|
|
* The getter function, if needed. ObjectAnimator hands off this functionality to
|
|
* PropertyValuesHolder, since it holds all of the per-property information. This
|
|
* property is automatically
|
|
* derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
|
|
* The getter is only derived and used if one of the values is null.
|
|
*/
|
|
private Method mGetter = null;
|
|
|
|
/**
|
|
* The type of values supplied. This information is used both in deriving the setter/getter
|
|
* functions and in deriving the type of TypeEvaluator.
|
|
*/
|
|
Class mValueType;
|
|
|
|
/**
|
|
* The set of keyframes (time/value pairs) that define this animation.
|
|
*/
|
|
KeyframeSet mKeyframeSet = null;
|
|
|
|
|
|
// type evaluators for the primitive types handled by this implementation
|
|
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
|
|
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
|
|
|
|
// We try several different types when searching for appropriate setter/getter functions.
|
|
// The caller may have supplied values in a type that does not match the setter/getter
|
|
// functions (such as the integers 0 and 1 to represent floating point values for alpha).
|
|
// Also, the use of generics in constructors means that we end up with the Object versions
|
|
// of primitive types (Float vs. float). But most likely, the setter/getter functions
|
|
// will take primitive types instead.
|
|
// So we supply an ordered array of other types to try before giving up.
|
|
private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
|
|
Double.class, Integer.class};
|
|
private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
|
|
Float.class, Double.class};
|
|
private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
|
|
Float.class, Integer.class};
|
|
|
|
// These maps hold all property entries for a particular class. This map
|
|
// is used to speed up property/setter/getter lookups for a given class/property
|
|
// combination. No need to use reflection on the combination more than once.
|
|
private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
|
|
new HashMap<Class, HashMap<String, Method>>();
|
|
private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
|
|
new HashMap<Class, HashMap<String, Method>>();
|
|
|
|
// This lock is used to ensure that only one thread is accessing the property maps
|
|
// at a time.
|
|
final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
|
|
|
|
// Used to pass single value to varargs parameter in setter invocation
|
|
final Object[] mTmpValueArray = new Object[1];
|
|
|
|
/**
|
|
* The type evaluator used to calculate the animated values. This evaluator is determined
|
|
* automatically based on the type of the start/end objects passed into the constructor,
|
|
* but the system only knows about the primitive types int and float. Any other
|
|
* type will need to set the evaluator to a custom evaluator for that type.
|
|
*/
|
|
private TypeEvaluator mEvaluator;
|
|
|
|
/**
|
|
* The value most recently calculated by calculateValue(). This is set during
|
|
* that function and might be retrieved later either by ValueAnimator.animatedValue() or
|
|
* by the property-setting logic in ObjectAnimator.animatedValue().
|
|
*/
|
|
private Object mAnimatedValue;
|
|
|
|
/**
|
|
* Internal utility constructor, used by the factory methods to set the property name.
|
|
* @param propertyName The name of the property for this holder.
|
|
*/
|
|
private PropertyValuesHolder(String propertyName) {
|
|
mPropertyName = propertyName;
|
|
}
|
|
|
|
/**
|
|
* Internal utility constructor, used by the factory methods to set the property.
|
|
* @param property The property for this holder.
|
|
*/
|
|
//private PropertyValuesHolder(Property property) {
|
|
// mProperty = property;
|
|
// if (property != null) {
|
|
// mPropertyName = property.getName();
|
|
// }
|
|
//}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder with a given property name and
|
|
* set of int values.
|
|
* @param propertyName The name of the property being animated.
|
|
* @param values The values that the named property will animate between.
|
|
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
|
|
*/
|
|
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
|
|
return new IntPropertyValuesHolder(propertyName, values);
|
|
}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder with a given property and
|
|
* set of int values.
|
|
* @param property The property being animated. Should not be null.
|
|
* @param values The values that the property will animate between.
|
|
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
|
|
*/
|
|
//public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
|
|
// return new IntPropertyValuesHolder(property, values);
|
|
//}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder with a given property name and
|
|
* set of float values.
|
|
* @param propertyName The name of the property being animated.
|
|
* @param values The values that the named property will animate between.
|
|
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
|
|
*/
|
|
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
|
|
return new FloatPropertyValuesHolder(propertyName, values);
|
|
}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder with a given property and
|
|
* set of float values.
|
|
* @param property The property being animated. Should not be null.
|
|
* @param values The values that the property will animate between.
|
|
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
|
|
*/
|
|
//public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
|
|
// return new FloatPropertyValuesHolder(property, values);
|
|
//}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder with a given property name and
|
|
* set of Object values. This variant also takes a TypeEvaluator because the system
|
|
* cannot automatically interpolate between objects of unknown type.
|
|
*
|
|
* @param propertyName The name of the property being animated.
|
|
* @param evaluator A TypeEvaluator that will be called on each animation frame to
|
|
* provide the necessary interpolation between the Object values to derive the animated
|
|
* value.
|
|
* @param values The values that the named property will animate between.
|
|
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
|
|
*/
|
|
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
|
|
Object... values) {
|
|
PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
|
|
pvh.setObjectValues(values);
|
|
pvh.setEvaluator(evaluator);
|
|
return pvh;
|
|
}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder with a given property and
|
|
* set of Object values. This variant also takes a TypeEvaluator because the system
|
|
* cannot automatically interpolate between objects of unknown type.
|
|
*
|
|
* @param property The property being animated. Should not be null.
|
|
* @param evaluator A TypeEvaluator that will be called on each animation frame to
|
|
* provide the necessary interpolation between the Object values to derive the animated
|
|
* value.
|
|
* @param values The values that the property will animate between.
|
|
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
|
|
*/
|
|
//public static <V> PropertyValuesHolder ofObject(Property property,
|
|
// TypeEvaluator<V> evaluator, V... values) {
|
|
// PropertyValuesHolder pvh = new PropertyValuesHolder(property);
|
|
// pvh.setObjectValues(values);
|
|
// pvh.setEvaluator(evaluator);
|
|
// return pvh;
|
|
//}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructs and returns a PropertyValuesHolder object with the specified property 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 the property's
|
|
* {@link android.util.Property#get(Object)} function.
|
|
* 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 with
|
|
* {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
|
|
* no way of determining what the value should be.
|
|
* @param property The property associated with this set of values. Should not be null.
|
|
* @param values The set of values to animate between.
|
|
*/
|
|
//public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
|
|
// KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
|
|
// if (keyframeSet instanceof IntKeyframeSet) {
|
|
// return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
|
|
// } else if (keyframeSet instanceof FloatKeyframeSet) {
|
|
// return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
|
|
// }
|
|
// else {
|
|
// PropertyValuesHolder pvh = new PropertyValuesHolder(property);
|
|
// pvh.mKeyframeSet = keyframeSet;
|
|
// pvh.mValueType = ((Keyframe)values[0]).getType();
|
|
// return pvh;
|
|
// }
|
|
//}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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 Keyframes.
|
|
*
|
|
* @param values One or more values that the animation will animate between.
|
|
*/
|
|
public void setKeyframes(Keyframe... values) {
|
|
int numKeyframes = values.length;
|
|
Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
|
|
mValueType = values[0].getType();
|
|
for (int i = 0; i < numKeyframes; ++i) {
|
|
keyframes[i] = values[i];
|
|
}
|
|
mKeyframeSet = new KeyframeSet(keyframes);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* Determine the setter or getter function using the JavaBeans convention of setFoo or
|
|
* getFoo for a property named 'foo'. This function figures out what the name of the
|
|
* function should be and uses reflection to find the Method with that name on the
|
|
* target object.
|
|
*
|
|
* @param targetClass The class to search for the method
|
|
* @param prefix "set" or "get", depending on whether we need a setter or getter.
|
|
* @param valueType The type of the parameter (in the case of a setter). This type
|
|
* is derived from the values set on this PropertyValuesHolder. This type is used as
|
|
* a first guess at the parameter type, but we check for methods with several different
|
|
* types to avoid problems with slight mis-matches between supplied values and actual
|
|
* value types used on the setter.
|
|
* @return Method the method associated with mPropertyName.
|
|
*/
|
|
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
|
|
// TODO: faster implementation...
|
|
Method returnVal = null;
|
|
String methodName = getMethodName(prefix, mPropertyName);
|
|
Class args[] = null;
|
|
if (valueType == null) {
|
|
try {
|
|
returnVal = targetClass.getMethod(methodName, args);
|
|
} catch (NoSuchMethodException e) {
|
|
Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " +
|
|
"Couldn't find no-arg method for property " + mPropertyName + ": " + e);
|
|
}
|
|
} else {
|
|
args = new Class[1];
|
|
Class typeVariants[];
|
|
if (mValueType.equals(Float.class)) {
|
|
typeVariants = FLOAT_VARIANTS;
|
|
} else if (mValueType.equals(Integer.class)) {
|
|
typeVariants = INTEGER_VARIANTS;
|
|
} else if (mValueType.equals(Double.class)) {
|
|
typeVariants = DOUBLE_VARIANTS;
|
|
} else {
|
|
typeVariants = new Class[1];
|
|
typeVariants[0] = mValueType;
|
|
}
|
|
for (Class typeVariant : typeVariants) {
|
|
args[0] = typeVariant;
|
|
try {
|
|
returnVal = targetClass.getMethod(methodName, args);
|
|
// change the value type to suit
|
|
mValueType = typeVariant;
|
|
return returnVal;
|
|
} catch (NoSuchMethodException e) {
|
|
// Swallow the error and keep trying other variants
|
|
}
|
|
}
|
|
// If we got here, then no appropriate function was found
|
|
Log.e("PropertyValuesHolder",
|
|
"Couldn't find " + prefix + "ter property " + mPropertyName +
|
|
" for " + targetClass.getSimpleName() +
|
|
" with value type "+ mValueType);
|
|
}
|
|
|
|
return returnVal;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the setter or getter requested. This utility function checks whether the
|
|
* requested method exists in the propertyMapMap cache. If not, it calls another
|
|
* utility function to request the Method from the targetClass directly.
|
|
* @param targetClass The Class on which the requested method should exist.
|
|
* @param propertyMapMap The cache of setters/getters derived so far.
|
|
* @param prefix "set" or "get", for the setter or getter.
|
|
* @param valueType The type of parameter passed into the method (null for getter).
|
|
* @return Method the method associated with mPropertyName.
|
|
*/
|
|
private Method setupSetterOrGetter(Class targetClass,
|
|
HashMap<Class, HashMap<String, Method>> propertyMapMap,
|
|
String prefix, Class valueType) {
|
|
Method setterOrGetter = null;
|
|
try {
|
|
// Have to lock property map prior to reading it, to guard against
|
|
// another thread putting something in there after we've checked it
|
|
// but before we've added an entry to it
|
|
mPropertyMapLock.writeLock().lock();
|
|
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
|
|
if (propertyMap != null) {
|
|
setterOrGetter = propertyMap.get(mPropertyName);
|
|
}
|
|
if (setterOrGetter == null) {
|
|
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
|
|
if (propertyMap == null) {
|
|
propertyMap = new HashMap<String, Method>();
|
|
propertyMapMap.put(targetClass, propertyMap);
|
|
}
|
|
propertyMap.put(mPropertyName, setterOrGetter);
|
|
}
|
|
} finally {
|
|
mPropertyMapLock.writeLock().unlock();
|
|
}
|
|
return setterOrGetter;
|
|
}
|
|
|
|
/**
|
|
* Utility function to get the setter from targetClass
|
|
* @param targetClass The Class on which the requested method should exist.
|
|
*/
|
|
void setupSetter(Class targetClass) {
|
|
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
|
|
}
|
|
|
|
/**
|
|
* Utility function to get the getter from targetClass
|
|
*/
|
|
private void setupGetter(Class targetClass) {
|
|
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
|
|
}
|
|
|
|
/**
|
|
* Internal function (called from ObjectAnimator) to set up the setter and getter
|
|
* prior to running the animation. If the setter has not been manually set for this
|
|
* object, it will be derived automatically given the property name, target object, and
|
|
* types of values supplied. If no getter has been set, it will be supplied iff any of the
|
|
* supplied values was null. If there is a null value, then the getter (supplied or derived)
|
|
* will be called to set those null values to the current value of the property
|
|
* on the target object.
|
|
* @param target The object on which the setter (and possibly getter) exist.
|
|
*/
|
|
void setupSetterAndGetter(Object target) {
|
|
//if (mProperty != null) {
|
|
// // check to make sure that mProperty is on the class of target
|
|
// try {
|
|
// Object testValue = mProperty.get(target);
|
|
// for (Keyframe kf : mKeyframeSet.mKeyframes) {
|
|
// if (!kf.hasValue()) {
|
|
// kf.setValue(mProperty.get(target));
|
|
// }
|
|
// }
|
|
// return;
|
|
// } catch (ClassCastException e) {
|
|
// Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
|
|
// ") on target object " + target + ". Trying reflection instead");
|
|
// mProperty = null;
|
|
// }
|
|
//}
|
|
Class targetClass = target.getClass();
|
|
if (mSetter == null) {
|
|
setupSetter(targetClass);
|
|
}
|
|
for (Keyframe kf : mKeyframeSet.mKeyframes) {
|
|
if (!kf.hasValue()) {
|
|
if (mGetter == null) {
|
|
setupGetter(targetClass);
|
|
}
|
|
try {
|
|
kf.setValue(mGetter.invoke(target));
|
|
} catch (InvocationTargetException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
} catch (IllegalAccessException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility function to set the value stored in a particular Keyframe. The value used is
|
|
* whatever the value is for the property name specified in the keyframe on the target object.
|
|
*
|
|
* @param target The target object from which the current value should be extracted.
|
|
* @param kf The keyframe which holds the property name and value.
|
|
*/
|
|
private void setupValue(Object target, Keyframe kf) {
|
|
//if (mProperty != null) {
|
|
// kf.setValue(mProperty.get(target));
|
|
//}
|
|
try {
|
|
if (mGetter == null) {
|
|
Class targetClass = target.getClass();
|
|
setupGetter(targetClass);
|
|
}
|
|
kf.setValue(mGetter.invoke(target));
|
|
} catch (InvocationTargetException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
} catch (IllegalAccessException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function is called by ObjectAnimator when setting the start values for an animation.
|
|
* The start values are set according to the current values in the target object. The
|
|
* property whose value is extracted is whatever is specified by the propertyName of this
|
|
* PropertyValuesHolder object.
|
|
*
|
|
* @param target The object which holds the start values that should be set.
|
|
*/
|
|
void setupStartValue(Object target) {
|
|
setupValue(target, mKeyframeSet.mKeyframes.get(0));
|
|
}
|
|
|
|
/**
|
|
* This function is called by ObjectAnimator when setting the end values for an animation.
|
|
* The end values are set according to the current values in the target object. The
|
|
* property whose value is extracted is whatever is specified by the propertyName of this
|
|
* PropertyValuesHolder object.
|
|
*
|
|
* @param target The object which holds the start values that should be set.
|
|
*/
|
|
void setupEndValue(Object target) {
|
|
setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
|
|
}
|
|
|
|
@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 to set the value on the target object, using the setter set up
|
|
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
|
|
* to handle turning the value calculated by ValueAnimator into a value set on the object
|
|
* according to the name of the property.
|
|
* @param target The target object on which the value is set
|
|
*/
|
|
void setAnimatedValue(Object target) {
|
|
//if (mProperty != null) {
|
|
// mProperty.set(target, getAnimatedValue());
|
|
//}
|
|
if (mSetter != null) {
|
|
try {
|
|
mTmpValueArray[0] = getAnimatedValue();
|
|
mSetter.invoke(target, mTmpValueArray);
|
|
} catch (InvocationTargetException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
} catch (IllegalAccessException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* Sets the name of the property that will be animated. This name is used to derive
|
|
* a setter function that will be called to set animated values.
|
|
* For example, a property name of <code>foo</code> will result
|
|
* in a call to the function <code>setFoo()</code> on the target object. If either
|
|
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
|
|
* also be derived and called.
|
|
*
|
|
* <p>Note that the setter function derived from this property name
|
|
* must take the same parameter type as the
|
|
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
|
|
* the setter function will fail.</p>
|
|
*
|
|
* @param propertyName The name of the property being animated.
|
|
*/
|
|
public void setPropertyName(String propertyName) {
|
|
mPropertyName = propertyName;
|
|
}
|
|
|
|
/**
|
|
* Sets the property that will be animated.
|
|
*
|
|
* <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
|
|
* must exist on the target object specified in that ObjectAnimator.</p>
|
|
*
|
|
* @param property The property being animated.
|
|
*/
|
|
//public void setProperty(Property property) {
|
|
// mProperty = property;
|
|
//}
|
|
|
|
/**
|
|
* Gets the name of the property that will be animated. This name will be used to derive
|
|
* a setter function that will be called to set animated values.
|
|
* For example, a property name of <code>foo</code> will result
|
|
* in a call to the function <code>setFoo()</code> on the target object. If either
|
|
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
|
|
* also be derived and called.
|
|
*/
|
|
public String getPropertyName() {
|
|
return mPropertyName;
|
|
}
|
|
|
|
/**
|
|
* Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
|
|
* most recently calculated in calculateValue().
|
|
* @return
|
|
*/
|
|
Object getAnimatedValue() {
|
|
return mAnimatedValue;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return mPropertyName + ": " + mKeyframeSet.toString();
|
|
}
|
|
|
|
/**
|
|
* Utility method to derive a setter/getter method name from a property name, where the
|
|
* prefix is typically "set" or "get" and the first letter of the property name is
|
|
* capitalized.
|
|
*
|
|
* @param prefix The precursor to the method name, before the property name begins, typically
|
|
* "set" or "get".
|
|
* @param propertyName The name of the property that represents the bulk of the method name
|
|
* after the prefix. The first letter of this word will be capitalized in the resulting
|
|
* method name.
|
|
* @return String the property name converted to a method name according to the conventions
|
|
* specified above.
|
|
*/
|
|
static String getMethodName(String prefix, String propertyName) {
|
|
if (propertyName == null || propertyName.length() == 0) {
|
|
// shouldn't get here
|
|
return prefix;
|
|
}
|
|
char firstLetter = Character.toUpperCase(propertyName.charAt(0));
|
|
String theRest = propertyName.substring(1);
|
|
return prefix + firstLetter + theRest;
|
|
}
|
|
|
|
static class IntPropertyValuesHolder extends PropertyValuesHolder {
|
|
|
|
// Cache JNI functions to avoid looking them up twice
|
|
//private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
|
|
// new HashMap<Class, HashMap<String, Integer>>();
|
|
//int mJniSetter;
|
|
//private IntProperty mIntProperty;
|
|
|
|
IntKeyframeSet mIntKeyframeSet;
|
|
int mIntAnimatedValue;
|
|
|
|
public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
|
|
super(propertyName);
|
|
mValueType = int.class;
|
|
mKeyframeSet = keyframeSet;
|
|
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
|
|
}
|
|
|
|
//public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
|
|
// super(property);
|
|
// mValueType = int.class;
|
|
// mKeyframeSet = keyframeSet;
|
|
// mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
|
|
// if (property instanceof IntProperty) {
|
|
// mIntProperty = (IntProperty) mProperty;
|
|
// }
|
|
//}
|
|
|
|
public IntPropertyValuesHolder(String propertyName, int... values) {
|
|
super(propertyName);
|
|
setIntValues(values);
|
|
}
|
|
|
|
//public IntPropertyValuesHolder(Property property, int... values) {
|
|
// super(property);
|
|
// setIntValues(values);
|
|
// if (property instanceof IntProperty) {
|
|
// mIntProperty = (IntProperty) mProperty;
|
|
// }
|
|
//}
|
|
|
|
@Override
|
|
public void setIntValues(int... values) {
|
|
super.setIntValues(values);
|
|
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
|
|
}
|
|
|
|
@Override
|
|
void calculateValue(float fraction) {
|
|
mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
|
|
}
|
|
|
|
@Override
|
|
Object getAnimatedValue() {
|
|
return mIntAnimatedValue;
|
|
}
|
|
|
|
@Override
|
|
public IntPropertyValuesHolder clone() {
|
|
IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
|
|
newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
|
|
return newPVH;
|
|
}
|
|
|
|
/**
|
|
* Internal function to set the value on the target object, using the setter set up
|
|
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
|
|
* to handle turning the value calculated by ValueAnimator into a value set on the object
|
|
* according to the name of the property.
|
|
* @param target The target object on which the value is set
|
|
*/
|
|
@Override
|
|
void setAnimatedValue(Object target) {
|
|
//if (mIntProperty != null) {
|
|
// mIntProperty.setValue(target, mIntAnimatedValue);
|
|
// return;
|
|
//}
|
|
//if (mProperty != null) {
|
|
// mProperty.set(target, mIntAnimatedValue);
|
|
// return;
|
|
//}
|
|
//if (mJniSetter != 0) {
|
|
// nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
|
|
// return;
|
|
//}
|
|
if (mSetter != null) {
|
|
try {
|
|
mTmpValueArray[0] = mIntAnimatedValue;
|
|
mSetter.invoke(target, mTmpValueArray);
|
|
} catch (InvocationTargetException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
} catch (IllegalAccessException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
void setupSetter(Class targetClass) {
|
|
//if (mProperty != null) {
|
|
// return;
|
|
//}
|
|
// Check new static hashmap<propName, int> for setter method
|
|
//try {
|
|
// mPropertyMapLock.writeLock().lock();
|
|
// HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
|
|
// if (propertyMap != null) {
|
|
// Integer mJniSetterInteger = propertyMap.get(mPropertyName);
|
|
// if (mJniSetterInteger != null) {
|
|
// mJniSetter = mJniSetterInteger;
|
|
// }
|
|
// }
|
|
// if (mJniSetter == 0) {
|
|
// String methodName = getMethodName("set", mPropertyName);
|
|
// mJniSetter = nGetIntMethod(targetClass, methodName);
|
|
// if (mJniSetter != 0) {
|
|
// if (propertyMap == null) {
|
|
// propertyMap = new HashMap<String, Integer>();
|
|
// sJNISetterPropertyMap.put(targetClass, propertyMap);
|
|
// }
|
|
// propertyMap.put(mPropertyName, mJniSetter);
|
|
// }
|
|
// }
|
|
//} catch (NoSuchMethodError e) {
|
|
// Log.d("PropertyValuesHolder",
|
|
// "Can't find native method using JNI, use reflection" + e);
|
|
//} finally {
|
|
// mPropertyMapLock.writeLock().unlock();
|
|
//}
|
|
//if (mJniSetter == 0) {
|
|
// Couldn't find method through fast JNI approach - just use reflection
|
|
super.setupSetter(targetClass);
|
|
//}
|
|
}
|
|
}
|
|
|
|
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
|
|
|
|
// Cache JNI functions to avoid looking them up twice
|
|
//private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
|
|
// new HashMap<Class, HashMap<String, Integer>>();
|
|
//int mJniSetter;
|
|
//private FloatProperty mFloatProperty;
|
|
|
|
FloatKeyframeSet mFloatKeyframeSet;
|
|
float mFloatAnimatedValue;
|
|
|
|
public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
|
|
super(propertyName);
|
|
mValueType = float.class;
|
|
mKeyframeSet = keyframeSet;
|
|
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
|
|
}
|
|
|
|
//public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
|
|
// super(property);
|
|
// mValueType = float.class;
|
|
// mKeyframeSet = keyframeSet;
|
|
// mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
|
|
// if (property instanceof FloatProperty) {
|
|
// mFloatProperty = (FloatProperty) mProperty;
|
|
// }
|
|
//}
|
|
|
|
public FloatPropertyValuesHolder(String propertyName, float... values) {
|
|
super(propertyName);
|
|
setFloatValues(values);
|
|
}
|
|
|
|
//public FloatPropertyValuesHolder(Property property, float... values) {
|
|
// super(property);
|
|
// setFloatValues(values);
|
|
// if (property instanceof FloatProperty) {
|
|
// mFloatProperty = (FloatProperty) mProperty;
|
|
// }
|
|
//}
|
|
|
|
@Override
|
|
public void setFloatValues(float... values) {
|
|
super.setFloatValues(values);
|
|
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
|
|
}
|
|
|
|
@Override
|
|
void calculateValue(float fraction) {
|
|
mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
|
|
}
|
|
|
|
@Override
|
|
Object getAnimatedValue() {
|
|
return mFloatAnimatedValue;
|
|
}
|
|
|
|
@Override
|
|
public FloatPropertyValuesHolder clone() {
|
|
FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
|
|
newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
|
|
return newPVH;
|
|
}
|
|
|
|
/**
|
|
* Internal function to set the value on the target object, using the setter set up
|
|
* earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
|
|
* to handle turning the value calculated by ValueAnimator into a value set on the object
|
|
* according to the name of the property.
|
|
* @param target The target object on which the value is set
|
|
*/
|
|
@Override
|
|
void setAnimatedValue(Object target) {
|
|
//if (mFloatProperty != null) {
|
|
// mFloatProperty.setValue(target, mFloatAnimatedValue);
|
|
// return;
|
|
//}
|
|
//if (mProperty != null) {
|
|
// mProperty.set(target, mFloatAnimatedValue);
|
|
// return;
|
|
//}
|
|
//if (mJniSetter != 0) {
|
|
// nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
|
|
// return;
|
|
//}
|
|
if (mSetter != null) {
|
|
try {
|
|
mTmpValueArray[0] = mFloatAnimatedValue;
|
|
mSetter.invoke(target, mTmpValueArray);
|
|
} catch (InvocationTargetException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
} catch (IllegalAccessException e) {
|
|
Log.e("PropertyValuesHolder", e.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
void setupSetter(Class targetClass) {
|
|
//if (mProperty != null) {
|
|
// return;
|
|
//}
|
|
// Check new static hashmap<propName, int> for setter method
|
|
//try {
|
|
// mPropertyMapLock.writeLock().lock();
|
|
// HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
|
|
// if (propertyMap != null) {
|
|
// Integer mJniSetterInteger = propertyMap.get(mPropertyName);
|
|
// if (mJniSetterInteger != null) {
|
|
// mJniSetter = mJniSetterInteger;
|
|
// }
|
|
// }
|
|
// if (mJniSetter == 0) {
|
|
// String methodName = getMethodName("set", mPropertyName);
|
|
// mJniSetter = nGetFloatMethod(targetClass, methodName);
|
|
// if (mJniSetter != 0) {
|
|
// if (propertyMap == null) {
|
|
// propertyMap = new HashMap<String, Integer>();
|
|
// sJNISetterPropertyMap.put(targetClass, propertyMap);
|
|
// }
|
|
// propertyMap.put(mPropertyName, mJniSetter);
|
|
// }
|
|
// }
|
|
//} catch (NoSuchMethodError e) {
|
|
// Log.d("PropertyValuesHolder",
|
|
// "Can't find native method using JNI, use reflection" + e);
|
|
//} finally {
|
|
// mPropertyMapLock.writeLock().unlock();
|
|
//}
|
|
//if (mJniSetter == 0) {
|
|
// Couldn't find method through fast JNI approach - just use reflection
|
|
super.setupSetter(targetClass);
|
|
//}
|
|
}
|
|
|
|
}
|
|
|
|
//native static private int nGetIntMethod(Class targetClass, String methodName);
|
|
//native static private int nGetFloatMethod(Class targetClass, String methodName);
|
|
//native static private void nCallIntMethod(Object target, int methodID, int arg);
|
|
//native static private void nCallFloatMethod(Object target, int methodID, float arg);
|
|
}
|