summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChet Haase <chet@google.com>2011-05-24 14:36:40 -0700
committerChet Haase <chet@google.com>2011-06-08 09:42:37 -0700
commitb39f051631250c49936a475d0e64584afb7f1b93 (patch)
tree9ba0a78cbdf4997965d4bc1b40e0a68a428433e3
parent3932aa7ff887007f4ae1e28e47b6775c9ceca566 (diff)
downloadframeworks_base-b39f051631250c49936a475d0e64584afb7f1b93.zip
frameworks_base-b39f051631250c49936a475d0e64584afb7f1b93.tar.gz
frameworks_base-b39f051631250c49936a475d0e64584afb7f1b93.tar.bz2
Add 'Property' object
This change adds a generic Property facility to the SDK, which allows an easy way to reference fields (private or otherwise) in a general way. For example, animations can use this facility to animate 'properties' on target objects in a way that is more code- and compiler-friendly than the existing String-based approach (for objects which have implemented Properties, of course). The animator classes have been updated to use this new approach (in addition to Strings, which are still more generally useful for objects which have get/set functions but not Property objects). The change also includes new Property objects on View (which can now be used in creating animations on Views). There is an unrelated change on GLES20RecordingCanvas to change the way we cache bitmaps, which avoids spurious garbage by using an ArrayList instead of a HashSet. Change-Id: I167b43a3fca20e7695b1a23ca81274367539acda
-rw-r--r--api/14.txt6
-rw-r--r--api/current.txt38
-rw-r--r--core/java/android/animation/FloatEvaluator.java8
-rw-r--r--core/java/android/animation/IntEvaluator.java8
-rw-r--r--core/java/android/animation/ObjectAnimator.java221
-rw-r--r--core/java/android/animation/PropertyValuesHolder.java213
-rw-r--r--core/java/android/animation/TypeEvaluator.java4
-rw-r--r--core/java/android/util/FloatProperty.java48
-rw-r--r--core/java/android/util/IntProperty.java48
-rw-r--r--core/java/android/util/NoSuchPropertyException.java30
-rw-r--r--core/java/android/util/Property.java106
-rw-r--r--core/java/android/util/ReflectiveProperty.java163
-rw-r--r--core/java/android/view/GLES20RecordingCanvas.java4
-rw-r--r--core/java/android/view/View.java165
14 files changed, 963 insertions, 99 deletions
diff --git a/api/14.txt b/api/14.txt
index ac93afb..ea09dc9 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -1921,12 +1921,12 @@ package android.animation {
public class FloatEvaluator implements android.animation.TypeEvaluator {
ctor public FloatEvaluator();
- method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+ method public java.lang.Float evaluate(float, java.lang.Number, java.lang.Number);
}
public class IntEvaluator implements android.animation.TypeEvaluator {
ctor public IntEvaluator();
- method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+ method public java.lang.Integer evaluate(float, java.lang.Integer, java.lang.Integer);
}
public abstract class Keyframe implements java.lang.Cloneable {
@@ -2012,7 +2012,7 @@ package android.animation {
}
public abstract interface TypeEvaluator {
- method public abstract java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+ method public abstract T evaluate(float, T, T);
}
public class ValueAnimator extends android.animation.Animator {
diff --git a/api/current.txt b/api/current.txt
index 80d0687..122b695 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1968,12 +1968,12 @@ package android.animation {
public class FloatEvaluator implements android.animation.TypeEvaluator {
ctor public FloatEvaluator();
- method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+ method public java.lang.Float evaluate(float, java.lang.Number, java.lang.Number);
}
public class IntEvaluator implements android.animation.TypeEvaluator {
ctor public IntEvaluator();
- method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+ method public java.lang.Integer evaluate(float, java.lang.Integer, java.lang.Integer);
}
public abstract class Keyframe implements java.lang.Cloneable {
@@ -2034,9 +2034,13 @@ package android.animation {
method public java.lang.String getPropertyName();
method public java.lang.Object getTarget();
method public static android.animation.ObjectAnimator ofFloat(java.lang.Object, java.lang.String, float...);
+ method public static android.animation.ObjectAnimator ofFloat(T, android.util.Property<T, java.lang.Float>, float...);
method public static android.animation.ObjectAnimator ofInt(java.lang.Object, java.lang.String, int...);
+ method public static android.animation.ObjectAnimator ofInt(T, android.util.Property<T, java.lang.Integer>, int...);
method public static android.animation.ObjectAnimator ofObject(java.lang.Object, java.lang.String, android.animation.TypeEvaluator, java.lang.Object...);
+ method public static android.animation.ObjectAnimator ofObject(T, android.util.Property<T, V>, android.animation.TypeEvaluator<V>, V...);
method public static android.animation.ObjectAnimator ofPropertyValuesHolder(java.lang.Object, android.animation.PropertyValuesHolder...);
+ method public void setProperty(android.util.Property);
method public void setPropertyName(java.lang.String);
}
@@ -2044,14 +2048,19 @@ package android.animation {
method public android.animation.PropertyValuesHolder clone();
method public java.lang.String getPropertyName();
method public static android.animation.PropertyValuesHolder ofFloat(java.lang.String, float...);
+ method public static android.animation.PropertyValuesHolder ofFloat(android.util.Property<?, java.lang.Float>, float...);
method public static android.animation.PropertyValuesHolder ofInt(java.lang.String, int...);
+ method public static android.animation.PropertyValuesHolder ofInt(android.util.Property<?, java.lang.Integer>, int...);
method public static android.animation.PropertyValuesHolder ofKeyframe(java.lang.String, android.animation.Keyframe...);
+ method public static android.animation.PropertyValuesHolder ofKeyframe(android.util.Property, android.animation.Keyframe...);
method public static android.animation.PropertyValuesHolder ofObject(java.lang.String, android.animation.TypeEvaluator, java.lang.Object...);
+ method public static android.animation.PropertyValuesHolder ofObject(android.util.Property, android.animation.TypeEvaluator<V>, V...);
method public void setEvaluator(android.animation.TypeEvaluator);
method public void setFloatValues(float...);
method public void setIntValues(int...);
method public void setKeyframes(android.animation.Keyframe...);
method public void setObjectValues(java.lang.Object...);
+ method public void setProperty(android.util.Property);
method public void setPropertyName(java.lang.String);
}
@@ -2060,7 +2069,7 @@ package android.animation {
}
public abstract interface TypeEvaluator {
- method public abstract java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+ method public abstract T evaluate(float, T, T);
}
public class ValueAnimator extends android.animation.Animator {
@@ -20096,6 +20105,10 @@ package android.util {
method public void previousMonth();
}
+ public class NoSuchPropertyException extends java.lang.RuntimeException {
+ ctor public NoSuchPropertyException(java.lang.String);
+ }
+
public class Pair {
ctor public Pair(F, S);
method public static android.util.Pair<A, B> create(A, B);
@@ -20131,6 +20144,16 @@ package android.util {
method public abstract void println(java.lang.String);
}
+ public abstract class Property {
+ ctor public Property(java.lang.Class<V>, java.lang.String);
+ method public abstract V get(T);
+ method public java.lang.String getName();
+ method public java.lang.Class<V> getType();
+ method public boolean isReadOnly();
+ method public static android.util.Property<T, V> of(java.lang.Class<T>, java.lang.Class<V>, java.lang.String);
+ method public void set(T, V);
+ }
+
public class SparseArray {
ctor public SparseArray();
ctor public SparseArray(int);
@@ -21877,6 +21900,11 @@ package android.view {
field protected static final int[] PRESSED_SELECTED_STATE_SET;
field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
+ field public static android.util.Property ROTATION;
+ field public static android.util.Property ROTATION_X;
+ field public static android.util.Property ROTATION_Y;
+ field public static android.util.Property SCALE_X;
+ field public static android.util.Property SCALE_Y;
field public static final int SCROLLBARS_INSIDE_INSET = 16777216; // 0x1000000
field public static final int SCROLLBARS_INSIDE_OVERLAY = 0; // 0x0
field public static final int SCROLLBARS_OUTSIDE_INSET = 50331648; // 0x3000000
@@ -21889,9 +21917,13 @@ package android.view {
field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
field public static final int STATUS_BAR_HIDDEN = 1; // 0x1
field public static final int STATUS_BAR_VISIBLE = 0; // 0x0
+ field public static android.util.Property TRANSLATION_X;
+ field public static android.util.Property TRANSLATION_Y;
field protected static final java.lang.String VIEW_LOG_TAG = "View";
field public static final int VISIBLE = 0; // 0x0
field protected static final int[] WINDOW_FOCUSED_STATE_SET;
+ field public static android.util.Property X;
+ field public static android.util.Property Y;
}
public static class View.BaseSavedState extends android.view.AbsSavedState {
diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java
index 9e2054d..9463aa1 100644
--- a/core/java/android/animation/FloatEvaluator.java
+++ b/core/java/android/animation/FloatEvaluator.java
@@ -19,7 +19,7 @@ package android.animation;
/**
* This evaluator can be used to perform type interpolation between <code>float</code> values.
*/
-public class FloatEvaluator implements TypeEvaluator {
+public class FloatEvaluator implements TypeEvaluator<Number> {
/**
* This function returns the result of linearly interpolating the start and end values, with
@@ -35,8 +35,8 @@ public class FloatEvaluator implements TypeEvaluator {
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
- public Object evaluate(float fraction, Object startValue, Object endValue) {
- float startFloat = ((Number) startValue).floatValue();
- return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
+ public Float evaluate(float fraction, Number startValue, Number endValue) {
+ float startFloat = startValue.floatValue();
+ return startFloat + fraction * (endValue.floatValue() - startFloat);
}
} \ No newline at end of file
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
index 7288927..34fb0dc 100644
--- a/core/java/android/animation/IntEvaluator.java
+++ b/core/java/android/animation/IntEvaluator.java
@@ -19,7 +19,7 @@ package android.animation;
/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
-public class IntEvaluator implements TypeEvaluator {
+public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* This function returns the result of linearly interpolating the start and end values, with
@@ -35,8 +35,8 @@ public class IntEvaluator implements TypeEvaluator {
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
- public Object evaluate(float fraction, Object startValue, Object endValue) {
- int startInt = ((Number) startValue).intValue();
- return (int) (startInt + fraction * (((Number) endValue).intValue() - startInt));
+ public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
+ int startInt = startValue;
+ return (int)(startInt + fraction * (endValue - startInt));
}
} \ No newline at end of file
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index b8a7cb2..31c5f8d 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -17,6 +17,7 @@
package android.animation;
import android.util.Log;
+import android.util.Property;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -39,6 +40,8 @@ public final class ObjectAnimator extends ValueAnimator {
private String mPropertyName;
+ private Property mProperty;
+
/**
* 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.
@@ -63,7 +66,7 @@ public final class ObjectAnimator extends ValueAnimator {
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
*
- * @param propertyName The name of the property being animated.
+ * @param propertyName The name of the property being animated. Should not be null.
*/
public void setPropertyName(String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
@@ -81,6 +84,31 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
+ * Sets the property that will be animated. Property objects will take precedence over
+ * properties specified by the {@link #setPropertyName(String)} method. Animations should
+ * be set up to use one or the other, not both.
+ *
+ * @param property The property being animated. Should not be null.
+ */
+ public void setProperty(Property property) {
+ // mValues could be null if this is being constructed piecemeal. Just record the
+ // propertyName to be used later when setValues() is called if so.
+ if (mValues != null) {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ String oldName = valuesHolder.getPropertyName();
+ valuesHolder.setProperty(property);
+ mValuesMap.remove(oldName);
+ mValuesMap.put(mPropertyName, valuesHolder);
+ }
+ if (mProperty != null) {
+ mPropertyName = property.getName();
+ }
+ mProperty = property;
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
* 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
@@ -93,36 +121,6 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
- * 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 prefix "set" or "get", depending on whether we need a setter or getter.
- * @return Method the method associated with mPropertyName.
- */
- private Method getPropertyFunction(String prefix, Class valueType) {
- // TODO: faster implementation...
- Method returnVal = null;
- String firstLetter = mPropertyName.substring(0, 1);
- String theRest = mPropertyName.substring(1);
- firstLetter = firstLetter.toUpperCase();
- String setterName = prefix + firstLetter + theRest;
- Class args[] = null;
- if (valueType != null) {
- args = new Class[1];
- args[0] = valueType;
- }
- try {
- returnVal = mTarget.getClass().getMethod(setterName, args);
- } catch (NoSuchMethodException e) {
- Log.e("ObjectAnimator",
- "Couldn't find setter/getter for property " + mPropertyName + ": " + e);
- }
- return returnVal;
- }
-
- /**
* Creates a new ObjectAnimator object. This default constructor is primarily for
* use internally; the other constructors which take parameters are more generally
* useful.
@@ -131,8 +129,8 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
- * A constructor that takes a single property name and set of values. This constructor is
- * used in the simple case of animating a single property.
+ * Private utility constructor that initializes the target object and name of the
+ * property being animated.
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
@@ -145,19 +143,29 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
+ * Private utility constructor that initializes the target object and property being animated.
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ */
+ private <T> ObjectAnimator(T target, Property<T, ?> property) {
+ mTarget = target;
+ setProperty(property);
+ }
+
+ /**
* Constructs and returns an ObjectAnimator that animates between int values. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
@@ -166,19 +174,36 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
+ * Constructs and returns an ObjectAnimator that animates between int values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
+ ObjectAnimator anim = new ObjectAnimator(target, property);
+ anim.setIntValues(values);
+ return anim;
+ }
+
+ /**
* Constructs and returns an ObjectAnimator that animates between float values. A single
- * value implies that that value is the one being animated to. However, this is not typically
- * useful in a ValueAnimator object because there is no way for the object to determine the
- * starting value for the animation (unlike ObjectAnimator, which can derive that value
- * from the target object and property being animated). Therefore, there should typically
- * be two or more values.
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
@@ -187,21 +212,40 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
- * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
- * be used when animating several properties at once with the same ObjectAnimator, since
- * PropertyValuesHolder allows you to associate a set of animation values with a property
- * name.
+ * Constructs and returns an ObjectAnimator that animates between float values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
+ *
+ * @param target The object whose property is to be animated.
+ * @param property The property being animated.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
+ float... values) {
+ ObjectAnimator anim = new ObjectAnimator(target, property);
+ anim.setFloatValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between Object values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
- * have public methods on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter for
- * each of the PropertyValuesHolder objects.
+ * have a public method on it called <code>setName()</code>, where <code>name</code> is
+ * the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param evaluator A TypeEvaluator that will be called on each animation frame to
- * provide the ncessry interpolation between the Object values to derive the animated
+ * provide the necessary interpolation between the Object values to derive the animated
* value.
- * @param values The PropertyValuesHolder objects which hold each the property name and values
- * to animate that property between.
+ * @param values A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator evaluator, Object... values) {
@@ -212,19 +256,44 @@ public final class ObjectAnimator extends ValueAnimator {
}
/**
- * Constructs and returns an ObjectAnimator that animates between the sets of values
- * specifed in <code>PropertyValueHolder</code> objects. This variant should
- * be used when animating several properties at once with the same ObjectAnimator, since
- * PropertyValuesHolder allows you to associate a set of animation values with a property
- * name.
+ * Constructs and returns an ObjectAnimator that animates between Object values. A single
+ * value implies that that value is the one being animated to. Two values imply a starting
+ * and ending values. More than two values imply a starting value, values to animate through
+ * along the way, and an ending value (these values will be distributed evenly across
+ * the duration of the animation).
*
- * @param target The object whose property is to be animated. This object should
- * have public methods on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter for
- * each of the PropertyValuesHolder objects.
- * @param values A set of PropertyValuesHolder objects whose values will be animated
- * between over time.
- * @return A ValueAnimator object that is set up to animate between the given values.
+ * @param target The object whose property is to be animated.
+ * @param property 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 A set of values that the animation will animate between over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
+ */
+ public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
+ TypeEvaluator<V> evaluator, V... values) {
+ ObjectAnimator anim = new ObjectAnimator(target, property);
+ anim.setObjectValues(values);
+ anim.setEvaluator(evaluator);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns an ObjectAnimator that animates between the sets of values specified
+ * in <code>PropertyValueHolder</code> objects. This variant should be used when animating
+ * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
+ * you to associate a set of animation values with a property name.
+ *
+ * @param target The object whose property is to be animated. Depending on how the
+ * PropertyValuesObjects were constructed, the target object should either have the {@link
+ * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
+ * PropertyValuesHOlder objects were created with property names) the target object should have
+ * public methods on it called <code>setName()</code>, where <code>name</code> is the name of
+ * the property passed in as the <code>propertyName</code> parameter for each of the
+ * PropertyValuesHolder objects.
+ * @param values A set of PropertyValuesHolder objects whose values will be animated between
+ * over time.
+ * @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
@@ -239,7 +308,11 @@ public final class ObjectAnimator extends ValueAnimator {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
- setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
+ if (mProperty != null) {
+ setValues(PropertyValuesHolder.ofInt(mProperty, values));
+ } else {
+ setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
+ }
} else {
super.setIntValues(values);
}
@@ -250,7 +323,11 @@ public final class ObjectAnimator extends ValueAnimator {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
- setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
+ if (mProperty != null) {
+ setValues(PropertyValuesHolder.ofFloat(mProperty, values));
+ } else {
+ setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
+ }
} else {
super.setFloatValues(values);
}
@@ -261,7 +338,11 @@ public final class ObjectAnimator extends ValueAnimator {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
- setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+ if (mProperty != null) {
+ setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
+ } else {
+ setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+ }
} else {
super.setObjectValues(values);
}
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 6f91fc0..58f23f7 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -16,7 +16,10 @@
package android.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;
@@ -39,6 +42,11 @@ public class PropertyValuesHolder implements Cloneable {
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
@@ -124,6 +132,17 @@ public class PropertyValuesHolder implements Cloneable {
}
/**
+ * 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.
@@ -131,8 +150,18 @@ public class PropertyValuesHolder implements Cloneable {
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
- PropertyValuesHolder pvh = new IntPropertyValuesHolder(propertyName, values);
- return pvh;
+ 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);
}
/**
@@ -143,18 +172,28 @@ public class PropertyValuesHolder implements Cloneable {
* @return PropertyValuesHolder The constructed PropertyValuesHolder object.
*/
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
- PropertyValuesHolder pvh = new FloatPropertyValuesHolder(propertyName, values);
- return pvh;
+ 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 interpolate between objects of unknown type.
+ * 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 ncessry interpolation between the Object values to derive the animated
+ * 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.
@@ -168,6 +207,26 @@ public class PropertyValuesHolder implements Cloneable {
}
/**
+ * 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
@@ -202,6 +261,37 @@ public class PropertyValuesHolder implements Cloneable {
}
/**
+ * 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
@@ -349,7 +439,6 @@ public class PropertyValuesHolder implements Cloneable {
// 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
- // TODO: can we store the setter/getter per Class instead of per Object?
mPropertyMapLock.writeLock().lock();
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
if (propertyMap != null) {
@@ -395,6 +484,22 @@ public class PropertyValuesHolder implements Cloneable {
* @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);
@@ -423,6 +528,9 @@ public class PropertyValuesHolder implements Cloneable {
* @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();
@@ -465,6 +573,7 @@ public class PropertyValuesHolder implements Cloneable {
try {
PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
newPVH.mPropertyName = mPropertyName;
+ newPVH.mProperty = mProperty;
newPVH.mKeyframeSet = mKeyframeSet.clone();
newPVH.mEvaluator = mEvaluator;
return newPVH;
@@ -482,6 +591,9 @@ public class PropertyValuesHolder implements Cloneable {
* @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();
@@ -558,6 +670,18 @@ public class PropertyValuesHolder implements Cloneable {
}
/**
+ * 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
@@ -597,17 +721,22 @@ public class PropertyValuesHolder implements Cloneable {
* specified above.
*/
static String getMethodName(String prefix, String propertyName) {
- char firstLetter = propertyName.charAt(0);
+ if (propertyName == null || propertyName.length() == 0) {
+ // shouldn't get here
+ return prefix;
+ }
+ char firstLetter = Character.toUpperCase(propertyName.charAt(0));
String theRest = propertyName.substring(1);
- firstLetter = Character.toUpperCase(firstLetter);
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;
@@ -619,11 +748,29 @@ public class PropertyValuesHolder implements Cloneable {
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);
@@ -656,6 +803,14 @@ public class PropertyValuesHolder implements Cloneable {
*/
@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;
@@ -674,6 +829,9 @@ public class PropertyValuesHolder implements Cloneable {
@Override
void setupSetter(Class targetClass) {
+ if (mProperty != null) {
+ return;
+ }
// Check new static hashmap<propName, int> for setter method
try {
mPropertyMapLock.writeLock().lock();
@@ -696,7 +854,8 @@ public class PropertyValuesHolder implements Cloneable {
}
}
} catch (NoSuchMethodError e) {
- // System.out.println("Can't find native method using JNI, use reflection" + e);
+ Log.d("PropertyValuesHolder",
+ "Can't find native method using JNI, use reflection" + e);
} finally {
mPropertyMapLock.writeLock().unlock();
}
@@ -709,9 +868,11 @@ public class PropertyValuesHolder implements Cloneable {
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;
@@ -723,11 +884,29 @@ public class PropertyValuesHolder implements Cloneable {
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);
@@ -760,6 +939,14 @@ public class PropertyValuesHolder implements Cloneable {
*/
@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;
@@ -778,6 +965,9 @@ public class PropertyValuesHolder implements Cloneable {
@Override
void setupSetter(Class targetClass) {
+ if (mProperty != null) {
+ return;
+ }
// Check new static hashmap<propName, int> for setter method
try {
mPropertyMapLock.writeLock().lock();
@@ -800,7 +990,8 @@ public class PropertyValuesHolder implements Cloneable {
}
}
} catch (NoSuchMethodError e) {
- // System.out.println("Can't find native method using JNI, use reflection" + e);
+ Log.d("PropertyValuesHolder",
+ "Can't find native method using JNI, use reflection" + e);
} finally {
mPropertyMapLock.writeLock().unlock();
}
diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java
index fa49175..e738da1 100644
--- a/core/java/android/animation/TypeEvaluator.java
+++ b/core/java/android/animation/TypeEvaluator.java
@@ -24,7 +24,7 @@ package android.animation;
*
* @see ValueAnimator#setEvaluator(TypeEvaluator)
*/
-public interface TypeEvaluator {
+public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
@@ -39,6 +39,6 @@ public interface TypeEvaluator {
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
- public Object evaluate(float fraction, Object startValue, Object endValue);
+ public T evaluate(float fraction, T startValue, T endValue);
} \ No newline at end of file
diff --git a/core/java/android/util/FloatProperty.java b/core/java/android/util/FloatProperty.java
new file mode 100644
index 0000000..a67b3cb
--- /dev/null
+++ b/core/java/android/util/FloatProperty.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 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 android.util;
+
+import android.util.Property;
+
+/**
+ * An implementation of {@link android.util.Property} to be used specifically with fields of type
+ * <code>float</code>. This type-specific subclass enables performance benefit by allowing
+ * calls to a {@link #set(Object, Float) set()} function that takes the primitive
+ * <code>float</code> type and avoids autoboxing and other overhead associated with the
+ * <code>Float</code> class.
+ *
+ * @param <T> The class on which the Property is declared.
+ *
+ * @hide
+ */
+public abstract class FloatProperty<T> extends Property<T, Float> {
+
+ public FloatProperty(String name) {
+ super(Float.class, name);
+ }
+
+ /**
+ * A type-specific override of the {@link #set(Object, Float)} that is faster when dealing
+ * with fields of type <code>float</code>.
+ */
+ public abstract void setValue(T object, float value);
+
+ @Override
+ final public void set(T object, Float value) {
+ setValue(object, value);
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/util/IntProperty.java b/core/java/android/util/IntProperty.java
new file mode 100644
index 0000000..459d6b2
--- /dev/null
+++ b/core/java/android/util/IntProperty.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 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 android.util;
+
+import android.util.Property;
+
+/**
+ * An implementation of {@link android.util.Property} to be used specifically with fields of type
+ * <code>int</code>. This type-specific subclass enables performance benefit by allowing
+ * calls to a {@link #set(Object, Integer) set()} function that takes the primitive
+ * <code>int</code> type and avoids autoboxing and other overhead associated with the
+ * <code>Integer</code> class.
+ *
+ * @param <T> The class on which the Property is declared.
+ *
+ * @hide
+ */
+public abstract class IntProperty<T> extends Property<T, Integer> {
+
+ public IntProperty(String name) {
+ super(Integer.class, name);
+ }
+
+ /**
+ * A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing
+ * with fields of type <code>int</code>.
+ */
+ public abstract void setValue(T object, int value);
+
+ @Override
+ final public void set(T object, Integer value) {
+ set(object, value.intValue());
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/util/NoSuchPropertyException.java b/core/java/android/util/NoSuchPropertyException.java
new file mode 100644
index 0000000..b93f983
--- /dev/null
+++ b/core/java/android/util/NoSuchPropertyException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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 android.util;
+
+/**
+ * Thrown when code requests a {@link Property} on a class that does
+ * not expose the appropriate method or field.
+ *
+ * @see Property#of(java.lang.Class, java.lang.Class, java.lang.String)
+ */
+public class NoSuchPropertyException extends RuntimeException {
+
+ public NoSuchPropertyException(String s) {
+ super(s);
+ }
+
+}
diff --git a/core/java/android/util/Property.java b/core/java/android/util/Property.java
new file mode 100644
index 0000000..458e4c3
--- /dev/null
+++ b/core/java/android/util/Property.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 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 android.util;
+
+
+/**
+ * A property is an abstraction that can be used to represent a <emb>mutable</em> value that is held
+ * in a <em>host</em> object. The Property's {@link #set(Object, Object)} or {@link #get(Object)}
+ * methods can be implemented in terms of the private fields of the host object, or via "setter" and
+ * "getter" methods or by some other mechanism, as appropriate.
+ *
+ * @param <T> The class on which the property is declared.
+ * @param <V> The type that this property represents.
+ */
+public abstract class Property<T, V> {
+
+ private final String mName;
+ private final Class<V> mType;
+
+ /**
+ * This factory method creates and returns a Property given the <code>class</code> and
+ * <code>name</code> parameters, where the <code>"name"</code> parameter represents either:
+ * <ul>
+ * <li>a public <code>getName()</code> method on the class which takes no arguments, plus an
+ * optional public <code>setName()</code> method which takes a value of the same type
+ * returned by <code>getName()</code>
+ * <li>a public <code>isName()</code> method on the class which takes no arguments, plus an
+ * optional public <code>setName()</code> method which takes a value of the same type
+ * returned by <code>isName()</code>
+ * <li>a public <code>name</code> field on the class
+ * </ul>
+ *
+ * <p>If either of the get/is method alternatives is found on the class, but an appropriate
+ * <code>setName()</code> method is not found, the <code>Property</code> will be
+ * {@link #isReadOnly() readOnly}. Calling the {@link #set(Object, Object)} method on such
+ * a property is allowed, but will have no effect.</p>
+ *
+ * <p>If neither the methods nor the field are found on the class a
+ * {@link NoSuchPropertyException} exception will be thrown.</p>
+ */
+ public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
+ return new ReflectiveProperty<T, V>(hostType, valueType, name);
+ }
+
+ /**
+ * A constructor that takes an identifying name and {@link #getType() type} for the property.
+ */
+ public Property(Class<V> type, String name) {
+ mName = name;
+ mType = type;
+ }
+
+ /**
+ * Returns true if the {@link #set(Object, Object)} method does not set the value on the target
+ * object (in which case the {@link #set(Object, Object) set()} method should throw a {@link
+ * NoSuchPropertyException} exception). This may happen if the Property wraps functionality that
+ * allows querying the underlying value but not setting it. For example, the {@link #of(Class,
+ * Class, String)} factory method may return a Property with name "foo" for an object that has
+ * only a <code>getFoo()</code> or <code>isFoo()</code> method, but no matching
+ * <code>setFoo()</code> method.
+ */
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ /**
+ * Sets the value on <code>object</code> which this property represents. If the method is unable
+ * to set the value on the target object, it will throw a {@link NoSuchPropertyException}
+ * exception.
+ */
+ public void set(T object, V value) {
+ throw new NoSuchPropertyException("Property is read-only; set() is not implemented");
+ }
+
+ /**
+ * Returns the current value that this property represents on the given <code>object</code>.
+ */
+ public abstract V get(T object);
+
+ /**
+ * Returns the name for this property.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the type for this property.
+ */
+ public Class<V> getType() {
+ return mType;
+ }
+}
diff --git a/core/java/android/util/ReflectiveProperty.java b/core/java/android/util/ReflectiveProperty.java
new file mode 100644
index 0000000..7bd7428
--- /dev/null
+++ b/core/java/android/util/ReflectiveProperty.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 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 android.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Internal class to automatically generate a Property for a given class/name pair, given the
+ * specification of {@link Property#of(java.lang.Class, java.lang.Class, java.lang.String)}
+ */
+class ReflectiveProperty<T, V> extends Property<T, V> {
+
+ private static final String PREFIX_GET = "get";
+ private static final String PREFIX_IS = "is";
+ private static final String PREFIX_SET = "set";
+ private Method mSetter;
+ private Method mGetter;
+ private Field mField;
+
+ /**
+ * For given property name 'name', look for getName/isName method or 'name' field.
+ * Also look for setName method (optional - could be readonly). Failing method getters and
+ * field results in throwing NoSuchPropertyException.
+ *
+ * @param propertyHolder The class on which the methods or field are found
+ * @param name The name of the property, where this name is capitalized and appended to
+ * "get" and "is to search for the appropriate methods. If the get/is methods are not found,
+ * the constructor will search for a field with that exact name.
+ */
+ public ReflectiveProperty(Class<T> propertyHolder, Class<V> valueType, String name) {
+ // TODO: cache reflection info for each new class/name pair
+ super(valueType, name);
+ char firstLetter = Character.toUpperCase(name.charAt(0));
+ String theRest = name.substring(1);
+ String capitalizedName = firstLetter + theRest;
+ String getterName = PREFIX_GET + capitalizedName;
+ try {
+ mGetter = propertyHolder.getMethod(getterName, (Class<?>[])null);
+ } catch (NoSuchMethodException e) {
+ // getName() not available - try isName() instead
+ getterName = PREFIX_IS + capitalizedName;
+ try {
+ mGetter = propertyHolder.getMethod(getterName, (Class<?>[])null);
+ } catch (NoSuchMethodException e1) {
+ // Try public field instead
+ try {
+ mField = propertyHolder.getField(name);
+ Class fieldType = mField.getType();
+ if (!typesMatch(valueType, fieldType)) {
+ throw new NoSuchPropertyException("Underlying type (" + fieldType + ") " +
+ "does not match Property type (" + valueType + ")");
+ }
+ return;
+ } catch (NoSuchFieldException e2) {
+ // no way to access property - throw appropriate exception
+ throw new NoSuchPropertyException("No accessor method or field found for"
+ + " property with name " + name);
+ }
+ }
+ }
+ Class getterType = mGetter.getReturnType();
+ // Check to make sure our getter type matches our valueType
+ if (!typesMatch(valueType, getterType)) {
+ throw new NoSuchPropertyException("Underlying type (" + getterType + ") " +
+ "does not match Property type (" + valueType + ")");
+ }
+ String setterName = PREFIX_SET + capitalizedName;
+ try {
+ mSetter = propertyHolder.getMethod(setterName, getterType);
+ } catch (NoSuchMethodException ignored) {
+ // Okay to not have a setter - just a readonly property
+ }
+ }
+
+ /**
+ * Utility method to check whether the type of the underlying field/method on the target
+ * object matches the type of the Property. The extra checks for primitive types are because
+ * generics will force the Property type to be a class, whereas the type of the underlying
+ * method/field will probably be a primitive type instead. Accept float as matching Float,
+ * etc.
+ */
+ private boolean typesMatch(Class<V> valueType, Class getterType) {
+ if (getterType != valueType) {
+ if (getterType.isPrimitive()) {
+ return (getterType == float.class && valueType == Float.class) ||
+ (getterType == int.class && valueType == Integer.class) ||
+ (getterType == boolean.class && valueType == Boolean.class) ||
+ (getterType == long.class && valueType == Long.class) ||
+ (getterType == double.class && valueType == Double.class) ||
+ (getterType == short.class && valueType == Short.class) ||
+ (getterType == byte.class && valueType == Byte.class) ||
+ (getterType == char.class && valueType == Character.class);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void set(T object, V value) {
+ if (mSetter != null) {
+ try {
+ mSetter.invoke(object, value);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError();
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ }
+ } else if (mField != null) {
+ try {
+ mField.set(object, value);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError();
+ }
+ } else {
+ throw new NoSuchPropertyException("Property is read-only; set() is not implemented");
+ }
+ }
+
+ @Override
+ public V get(T object) {
+ if (mGetter != null) {
+ try {
+ return (V) mGetter.invoke(object, (Object[])null);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError();
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ }
+ } else if (mField != null) {
+ try {
+ return (V) mField.get(object);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError();
+ }
+ }
+ // Should not get here: there should always be a non-null getter or field
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns false if there is no setter or public field underlying this Property.
+ */
+ @Override
+ public boolean isReadOnly() {
+ return (mSetter == null && mField == null);
+ }
+}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 2603281..ec94fe7 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -25,7 +25,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
-import java.util.HashSet;
+import java.util.ArrayList;
/**
* An implementation of a GL canvas that records drawing operations.
@@ -37,7 +37,7 @@ class GLES20RecordingCanvas extends GLES20Canvas {
// These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
// as the DisplayList is alive.
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
- private final HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>();
+ private final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
GLES20RecordingCanvas(boolean translucent) {
super(true, translucent);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 70218ac..74926bb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,8 @@
package android.view;
+import android.util.FloatProperty;
+import android.util.Property;
import com.android.internal.R;
import com.android.internal.util.Predicate;
import com.android.internal.view.menu.MenuBuilder;
@@ -12436,6 +12438,169 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
return getVerticalScrollFactor();
}
+ //
+ // Properties
+ //
+ /**
+ * A Property wrapper around the <code>alpha</code> functionality handled by the
+ * {@link View#setAlpha(float)} and {@link View#getAlpha()} methods.
+ */
+ static Property<View, Float> ALPHA = new FloatProperty<View>("alpha") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setAlpha(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getAlpha();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>translationX</code> functionality handled by the
+ * {@link View#setTranslationX(float)} and {@link View#getTranslationX()} methods.
+ */
+ public static Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setTranslationX(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getTranslationX();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>translationY</code> functionality handled by the
+ * {@link View#setTranslationY(float)} and {@link View#getTranslationY()} methods.
+ */
+ public static Property<View, Float> TRANSLATION_Y = new FloatProperty<View>("translationY") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setTranslationY(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getTranslationY();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>x</code> functionality handled by the
+ * {@link View#setX(float)} and {@link View#getX()} methods.
+ */
+ public static Property<View, Float> X = new FloatProperty<View>("x") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setX(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getX();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>y</code> functionality handled by the
+ * {@link View#setY(float)} and {@link View#getY()} methods.
+ */
+ public static Property<View, Float> Y = new FloatProperty<View>("y") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setY(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getY();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>rotation</code> functionality handled by the
+ * {@link View#setRotation(float)} and {@link View#getRotation()} methods.
+ */
+ public static Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setRotation(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getRotation();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>rotationX</code> functionality handled by the
+ * {@link View#setRotationX(float)} and {@link View#getRotationX()} methods.
+ */
+ public static Property<View, Float> ROTATION_X = new FloatProperty<View>("rotationX") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setRotationX(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getRotationX();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>rotationY</code> functionality handled by the
+ * {@link View#setRotationY(float)} and {@link View#getRotationY()} methods.
+ */
+ public static Property<View, Float> ROTATION_Y = new FloatProperty<View>("rotationY") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setRotationY(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getRotationY();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>scaleX</code> functionality handled by the
+ * {@link View#setScaleX(float)} and {@link View#getScaleX()} methods.
+ */
+ public static Property<View, Float> SCALE_X = new FloatProperty<View>("scaleX") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setScaleX(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getScaleX();
+ }
+ };
+
+ /**
+ * A Property wrapper around the <code>scaleY</code> functionality handled by the
+ * {@link View#setScaleY(float)} and {@link View#getScaleY()} methods.
+ */
+ public static Property<View, Float> SCALE_Y = new FloatProperty<View>("scaleY") {
+ @Override
+ public void setValue(View object, float value) {
+ object.setScaleY(value);
+ }
+
+ @Override
+ public Float get(View object) {
+ return object.getScaleY();
+ }
+ };
+
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.
* Each MeasureSpec represents a requirement for either the width or the height.