From 4eed52944c0fcb3afa7369aba60fb5c655580286 Mon Sep 17 00:00:00 2001 From: George Mount Date: Fri, 30 Aug 2013 13:56:01 -0700 Subject: [PATCH] Support for multiple value animations. Bug 2805977 Support for multi-value setters in ObjectAnimator. Change-Id: I7854dfcb82372afc6d9afb4c50a289ff9d41574e --- api/current.txt | 22 ++ .../animation/FloatArrayEvaluator.java | 77 ++++ .../android/animation/IntArrayEvaluator.java | 75 ++++ .../android/animation/ObjectAnimator.java | 95 +++++ .../animation/PropertyValuesHolder.java | 374 ++++++++++++++++++ ...android_animation_PropertyValuesHolder.cpp | 98 ++++- 6 files changed, 740 insertions(+), 1 deletion(-) create mode 100644 core/java/android/animation/FloatArrayEvaluator.java create mode 100644 core/java/android/animation/IntArrayEvaluator.java diff --git a/api/current.txt b/api/current.txt index 3124322dcb6dd..0a9694d7af53c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2433,11 +2433,23 @@ package android.animation { method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object); } + public class FloatArrayEvaluator implements android.animation.TypeEvaluator { + ctor public FloatArrayEvaluator(); + ctor public FloatArrayEvaluator(float[]); + method public float[] evaluate(float, float[], float[]); + } + public class FloatEvaluator implements android.animation.TypeEvaluator { ctor public FloatEvaluator(); method public java.lang.Float evaluate(float, java.lang.Number, java.lang.Number); } + public class IntArrayEvaluator implements android.animation.TypeEvaluator { + ctor public IntArrayEvaluator(); + ctor public IntArrayEvaluator(int[]); + method public int[] evaluate(float, int[], int[]); + } + public class IntEvaluator implements android.animation.TypeEvaluator { ctor public IntEvaluator(); method public java.lang.Integer evaluate(float, java.lang.Integer, java.lang.Integer); @@ -2510,6 +2522,10 @@ package android.animation { method public static android.animation.ObjectAnimator ofFloat(T, android.util.Property, 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, int...); + method public static android.animation.ObjectAnimator ofMultiFloat(java.lang.Object, java.lang.String, float[][]); + method public static android.animation.ObjectAnimator ofMultiFloat(java.lang.Object, java.lang.String, android.animation.TypeConverter, android.animation.TypeEvaluator, T...); + method public static android.animation.ObjectAnimator ofMultiInt(java.lang.Object, java.lang.String, int[][]); + method public static android.animation.ObjectAnimator ofMultiInt(java.lang.Object, java.lang.String, android.animation.TypeConverter, android.animation.TypeEvaluator, T...); 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, android.animation.TypeEvaluator, V...); method public static android.animation.ObjectAnimator ofObject(T, android.util.Property, android.animation.TypeConverter, android.animation.TypeEvaluator, V...); @@ -2528,6 +2544,12 @@ package android.animation { method public static android.animation.PropertyValuesHolder ofInt(android.util.Property, 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 ofMultiFloat(java.lang.String, float[][]); + method public static android.animation.PropertyValuesHolder ofMultiFloat(java.lang.String, android.animation.TypeConverter, android.animation.TypeEvaluator, V...); + method public static android.animation.PropertyValuesHolder ofMultiFloat(java.lang.String, android.animation.TypeConverter, android.animation.TypeEvaluator, android.animation.Keyframe...); + method public static android.animation.PropertyValuesHolder ofMultiInt(java.lang.String, int[][]); + method public static android.animation.PropertyValuesHolder ofMultiInt(java.lang.String, android.animation.TypeConverter, android.animation.TypeEvaluator, V...); + method public static android.animation.PropertyValuesHolder ofMultiInt(java.lang.String, android.animation.TypeConverter, android.animation.TypeEvaluator, 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...); method public static android.animation.PropertyValuesHolder ofObject(android.util.Property, android.animation.TypeConverter, android.animation.TypeEvaluator, T...); diff --git a/core/java/android/animation/FloatArrayEvaluator.java b/core/java/android/animation/FloatArrayEvaluator.java new file mode 100644 index 0000000000000..9ae1197ba1ec4 --- /dev/null +++ b/core/java/android/animation/FloatArrayEvaluator.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 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.animation; + +/** + * This evaluator can be used to perform type interpolation between float[] values. + * Each index into the array is treated as a separate value to interpolate. For example, + * evaluating {100, 200} and {300, 400} will interpolate the value at + * the first index between 100 and 300 and the value at the second index value between 200 and 400. + */ +public class FloatArrayEvaluator implements TypeEvaluator { + + private float[] mArray; + + /** + * Create a FloatArrayEvaluator that does not reuse the animated value. Care must be taken + * when using this option because on every evaluation a new float[] will be + * allocated. + * + * @see #FloatArrayEvaluator(float[]) + */ + public FloatArrayEvaluator() { + } + + /** + * Create a FloatArrayEvaluator that reuses reuseArray for every evaluate() call. + * Caution must be taken to ensure that the value returned from + * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or + * used across threads. The value will be modified on each evaluate() call. + * + * @param reuseArray The array to modify and return from evaluate. + */ + public FloatArrayEvaluator(float[] reuseArray) { + mArray = reuseArray; + } + + /** + * Interpolates the value at each index by the fraction. If + * {@link #FloatArrayEvaluator(float[])} was used to construct this object, + * reuseArray will be returned, otherwise a new float[] + * will be returned. + * + * @param fraction The fraction from the starting to the ending values + * @param startValue The start value. + * @param endValue The end value. + * @return A float[] where each element is an interpolation between + * the same index in startValue and endValue. + */ + @Override + public float[] evaluate(float fraction, float[] startValue, float[] endValue) { + float[] array = mArray; + if (array == null) { + array = new float[startValue.length]; + } + + for (int i = 0; i < array.length; i++) { + float start = startValue[i]; + float end = endValue[i]; + array[i] = start + (fraction * (end - start)); + } + return array; + } +} diff --git a/core/java/android/animation/IntArrayEvaluator.java b/core/java/android/animation/IntArrayEvaluator.java new file mode 100644 index 0000000000000..d7f10f3af3c65 --- /dev/null +++ b/core/java/android/animation/IntArrayEvaluator.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 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.animation; + +/** + * This evaluator can be used to perform type interpolation between int[] values. + * Each index into the array is treated as a separate value to interpolate. For example, + * evaluating {100, 200} and {300, 400} will interpolate the value at + * the first index between 100 and 300 and the value at the second index value between 200 and 400. + */ +public class IntArrayEvaluator implements TypeEvaluator { + + private int[] mArray; + + /** + * Create an IntArrayEvaluator that does not reuse the animated value. Care must be taken + * when using this option because on every evaluation a new int[] will be + * allocated. + * + * @see #IntArrayEvaluator(int[]) + */ + public IntArrayEvaluator() { + } + + /** + * Create an IntArrayEvaluator that reuses reuseArray for every evaluate() call. + * Caution must be taken to ensure that the value returned from + * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or + * used across threads. The value will be modified on each evaluate() call. + * + * @param reuseArray The array to modify and return from evaluate. + */ + public IntArrayEvaluator(int[] reuseArray) { + mArray = reuseArray; + } + + /** + * Interpolates the value at each index by the fraction. If {@link #IntArrayEvaluator(int[])} + * was used to construct this object, reuseArray will be returned, otherwise + * a new int[] will be returned. + * + * @param fraction The fraction from the starting to the ending values + * @param startValue The start value. + * @param endValue The end value. + * @return An int[] where each element is an interpolation between + * the same index in startValue and endValue. + */ + @Override + public int[] evaluate(float fraction, int[] startValue, int[] endValue) { + int[] array = mArray; + if (array == null) { + array = new int[startValue.length]; + } + for (int i = 0; i < array.length; i++) { + int start = startValue[i]; + int end = endValue[i]; + array[i] = (int) (start + (fraction * (end - start))); + } + return array; + } +} diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index f930f2259a9b1..07a2821724a6f 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -227,6 +227,53 @@ public final class ObjectAnimator extends ValueAnimator { return anim; } + /** + * Constructs and returns an ObjectAnimator that animates over int values for a multiple + * parameters setter. Only public methods that take only int parameters are supported. + * Each int[] contains a complete set of parameters to the setter method. + * At least two int[] values must be provided, a start and end. 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 may + * have a public method on it called setName(), where name is + * the value of the propertyName parameter. propertyName may also + * be the case-sensitive complete name of the public setter method. + * @param propertyName The name of the property being animated or the name of the setter method. + * @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 ofMultiInt(Object target, String propertyName, int[][] values) { + PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiInt(propertyName, values); + return ofPropertyValuesHolder(target, pvh); + } + + /** + * Constructs and returns an ObjectAnimator that animates over values for a multiple int + * parameters setter. Only public methods that take only int parameters are supported. + *

At least two values must be provided, a start and end. 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 may + * have a public method on it called setName(), where name is + * the value of the propertyName parameter. propertyName may also + * be the complete name of the public method. + * @param propertyName The name of the property being animated or the name of the setter method. + * @param converter Converts T objects into int parameters for the multi-value setter. + * @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 ObjectAnimator ofMultiInt(Object target, String propertyName, + TypeConverter converter, TypeEvaluator evaluator, T... values) { + PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiInt(propertyName, converter, + evaluator, values); + return ObjectAnimator.ofPropertyValuesHolder(target, pvh); + } + /** * 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 starting @@ -266,6 +313,54 @@ public final class ObjectAnimator extends ValueAnimator { return anim; } + /** + * Constructs and returns an ObjectAnimator that animates over float values for a multiple + * parameters setter. Only public methods that take only float parameters are supported. + * Each float[] contains a complete set of parameters to the setter method. + * At least two float[] values must be provided, a start and end. 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 may + * have a public method on it called setName(), where name is + * the value of the propertyName parameter. propertyName may also + * be the complete name of the public method. + * @param propertyName The name of the property being animated or the name of the setter method. + * @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 ofMultiFloat(Object target, String propertyName, + float[][] values) { + PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiFloat(propertyName, values); + return ofPropertyValuesHolder(target, pvh); + } + + /** + * Constructs and returns an ObjectAnimator that animates over values for a multiple float + * parameters setter. Only public methods that take only float parameters are supported. + *

At least two values must be provided, a start and end. 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 may + * have a public method on it called setName(), where name is + * the value of the propertyName parameter. propertyName may also + * be the case-sensitive complete name of the public setter method. + * @param propertyName The name of the property being animated or the name of the setter method. + * @param converter Converts T objects into float parameters for the multi-value setter. + * @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 ObjectAnimator ofMultiFloat(Object target, String propertyName, + TypeConverter converter, TypeEvaluator evaluator, T... values) { + PropertyValuesHolder pvh = PropertyValuesHolder.ofMultiFloat(propertyName, converter, + evaluator, values); + return ObjectAnimator.ofPropertyValuesHolder(target, pvh); + } + /** * 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 starting diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 5f5e48584c338..c291970af952b 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -169,6 +169,84 @@ public class PropertyValuesHolder implements Cloneable { return new IntPropertyValuesHolder(property, values); } + /** + * Constructs and returns a PropertyValuesHolder with a given property name and + * set of int[] values. At least two int[] values must be supplied, + * a start and end value. If more values are supplied, the values will be animated from the + * start, through all intermediate values to the end value. When used with ObjectAnimator, + * the elements of the array represent the parameters of the setter function. + * + * @param propertyName The name of the property being animated. Can also be the name of the + * entire setter method. Should not be null. + * @param values The values that the property will animate between. + * @return PropertyValuesHolder The constructed PropertyValuesHolder object. + * @see IntArrayEvaluator#IntArrayEvaluator(int[]) + * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[]) + */ + public static PropertyValuesHolder ofMultiInt(String propertyName, int[][] values) { + if (values.length < 2) { + throw new IllegalArgumentException("At least 2 values must be supplied"); + } + int numParameters = 0; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + throw new IllegalArgumentException("values must not be null"); + } + int length = values[i].length; + if (i == 0) { + numParameters = length; + } else if (length != numParameters) { + throw new IllegalArgumentException("Values must all have the same length"); + } + } + IntArrayEvaluator evaluator = new IntArrayEvaluator(new int[numParameters]); + return new MultiIntValuesHolder(propertyName, null, evaluator, (Object[]) values); + } + + /** + * Constructs and returns a PropertyValuesHolder with a given property and + * set of Object values for use with ObjectAnimator multi-value setters. The Object + * values are converted to int[] using the converter. + * + * @param propertyName The property being animated or complete name of the setter. + * Should not be null. + * @param converter Used to convert the animated value to setter parameters. + * @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. + * @see ObjectAnimator#ofMultiInt(Object, String, TypeConverter, TypeEvaluator, Object[]) + * @see ObjectAnimator#ofPropertyValuesHolder(Object, PropertyValuesHolder...) + */ + public static PropertyValuesHolder ofMultiInt(String propertyName, + TypeConverter converter, TypeEvaluator evaluator, V... values) { + return new MultiIntValuesHolder(propertyName, converter, evaluator, values); + } + + /** + * Constructs and returns a PropertyValuesHolder object with the specified property name or + * setter name for use in a multi-int setter function using ObjectAnimator. The values can be + * of any type, but the type should be consistent so that the supplied + * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The + * converter converts the values to parameters in the setter function. + * + *

At least two values must be supplied, a start and an end value.

+ * + * @param propertyName The name of the property to associate with the set of values. This + * may also be the complete name of a setter function. + * @param converter Converts values into int parameters for the setter. + * Can be null if the Keyframes have int[] values. + * @param evaluator Used to interpolate between values. + * @param values The values at specific fractional times to evaluate between + * @return A PropertyValuesHolder for a multi-int parameter setter. + */ + public static PropertyValuesHolder ofMultiInt(String propertyName, + TypeConverter converter, TypeEvaluator evaluator, Keyframe... values) { + KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); + return new MultiIntValuesHolder(propertyName, converter, evaluator, keyframeSet); + } + /** * Constructs and returns a PropertyValuesHolder with a given property name and * set of float values. @@ -191,6 +269,83 @@ public class PropertyValuesHolder implements Cloneable { return new FloatPropertyValuesHolder(property, values); } + /** + * Constructs and returns a PropertyValuesHolder with a given property name and + * set of float[] values. At least two float[] values must be supplied, + * a start and end value. If more values are supplied, the values will be animated from the + * start, through all intermediate values to the end value. When used with ObjectAnimator, + * the elements of the array represent the parameters of the setter function. + * + * @param propertyName The name of the property being animated. Can also be the name of the + * entire setter method. Should not be null. + * @param values The values that the property will animate between. + * @return PropertyValuesHolder The constructed PropertyValuesHolder object. + * @see FloatArrayEvaluator#FloatArrayEvaluator(float[]) + * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[]) + */ + public static PropertyValuesHolder ofMultiFloat(String propertyName, float[][] values) { + if (values.length < 2) { + throw new IllegalArgumentException("At least 2 values must be supplied"); + } + int numParameters = 0; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + throw new IllegalArgumentException("values must not be null"); + } + int length = values[i].length; + if (i == 0) { + numParameters = length; + } else if (length != numParameters) { + throw new IllegalArgumentException("Values must all have the same length"); + } + } + FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[numParameters]); + return new MultiFloatValuesHolder(propertyName, null, evaluator, (Object[]) values); + } + + /** + * Constructs and returns a PropertyValuesHolder with a given property and + * set of Object values for use with ObjectAnimator multi-value setters. The Object + * values are converted to float[] using the converter. + * + * @param propertyName The property being animated or complete name of the setter. + * Should not be null. + * @param converter Used to convert the animated value to setter parameters. + * @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. + * @see ObjectAnimator#ofMultiFloat(Object, String, TypeConverter, TypeEvaluator, Object[]) + */ + public static PropertyValuesHolder ofMultiFloat(String propertyName, + TypeConverter converter, TypeEvaluator evaluator, V... values) { + return new MultiFloatValuesHolder(propertyName, converter, evaluator, values); + } + + /** + * Constructs and returns a PropertyValuesHolder object with the specified property name or + * setter name for use in a multi-float setter function using ObjectAnimator. The values can be + * of any type, but the type should be consistent so that the supplied + * {@link android.animation.TypeEvaluator} can be used to to evaluate the animated value. The + * converter converts the values to parameters in the setter function. + * + *

At least two values must be supplied, a start and an end value.

+ * + * @param propertyName The name of the property to associate with the set of values. This + * may also be the complete name of a setter function. + * @param converter Converts values into float parameters for the setter. + * Can be null if the Keyframes have float[] values. + * @param evaluator Used to interpolate between values. + * @param values The values at specific fractional times to evaluate between + * @return A PropertyValuesHolder for a multi-float parameter setter. + */ + public static PropertyValuesHolder ofMultiFloat(String propertyName, + TypeConverter converter, TypeEvaluator evaluator, Keyframe... values) { + KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values); + return new MultiFloatValuesHolder(propertyName, converter, evaluator, keyframeSet); + } + /** * Constructs and returns a PropertyValuesHolder with a given property name and * set of Object values. This variant also takes a TypeEvaluator because the system @@ -1078,8 +1233,227 @@ public class PropertyValuesHolder implements Cloneable { } + static class MultiFloatValuesHolder extends PropertyValuesHolder { + private int mJniSetter; + private static final HashMap> sJNISetterPropertyMap = + new HashMap>(); + + public MultiFloatValuesHolder(String propertyName, TypeConverter converter, + TypeEvaluator evaluator, Object... values) { + super(propertyName); + setConverter(converter); + setObjectValues(values); + setEvaluator(evaluator); + } + + public MultiFloatValuesHolder(String propertyName, TypeConverter converter, + TypeEvaluator evaluator, KeyframeSet keyframeSet) { + super(propertyName); + setConverter(converter); + mKeyframeSet = keyframeSet; + setEvaluator(evaluator); + } + + /** + * 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) { + float[] values = (float[]) getAnimatedValue(); + int numParameters = values.length; + if (mJniSetter != 0) { + switch (numParameters) { + case 1: + nCallFloatMethod(target, mJniSetter, values[0]); + break; + case 2: + nCallTwoFloatMethod(target, mJniSetter, values[0], values[1]); + break; + case 4: + nCallFourFloatMethod(target, mJniSetter, values[0], values[1], + values[2], values[3]); + break; + default: { + nCallMultipleFloatMethod(target, mJniSetter, values); + break; + } + } + } + } + + /** + * Internal function (called from ObjectAnimator) to set up the setter and getter + * prior to running the animation. No getter can be used for multiple parameters. + * + * @param target The object on which the setter exists. + */ + @Override + void setupSetterAndGetter(Object target) { + setupSetter(target.getClass()); + } + + @Override + void setupSetter(Class targetClass) { + if (mJniSetter != 0) { + return; + } + try { + mPropertyMapLock.writeLock().lock(); + HashMap propertyMap = sJNISetterPropertyMap.get(targetClass); + if (propertyMap != null) { + Integer jniSetterInteger = propertyMap.get(mPropertyName); + if (jniSetterInteger != null) { + mJniSetter = jniSetterInteger; + } + } + if (mJniSetter == 0) { + String methodName = getMethodName("set", mPropertyName); + calculateValue(0f); + float[] values = (float[]) getAnimatedValue(); + int numParams = values.length; + try { + mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams); + } catch (NoSuchMethodError e) { + // try without the 'set' prefix + mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName, numParams); + } + if (mJniSetter != 0) { + if (propertyMap == null) { + propertyMap = new HashMap(); + sJNISetterPropertyMap.put(targetClass, propertyMap); + } + propertyMap.put(mPropertyName, mJniSetter); + } + } + } finally { + mPropertyMapLock.writeLock().unlock(); + } + } + } + + static class MultiIntValuesHolder extends PropertyValuesHolder { + private int mJniSetter; + private static final HashMap> sJNISetterPropertyMap = + new HashMap>(); + + public MultiIntValuesHolder(String propertyName, TypeConverter converter, + TypeEvaluator evaluator, Object... values) { + super(propertyName); + setConverter(converter); + setObjectValues(values); + setEvaluator(evaluator); + } + + public MultiIntValuesHolder(String propertyName, TypeConverter converter, + TypeEvaluator evaluator, KeyframeSet keyframeSet) { + super(propertyName); + setConverter(converter); + mKeyframeSet = keyframeSet; + setEvaluator(evaluator); + } + + /** + * 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) { + int[] values = (int[]) getAnimatedValue(); + int numParameters = values.length; + if (mJniSetter != 0) { + switch (numParameters) { + case 1: + nCallIntMethod(target, mJniSetter, values[0]); + break; + case 2: + nCallTwoIntMethod(target, mJniSetter, values[0], values[1]); + break; + case 4: + nCallFourIntMethod(target, mJniSetter, values[0], values[1], + values[2], values[3]); + break; + default: { + nCallMultipleIntMethod(target, mJniSetter, values); + break; + } + } + } + } + + /** + * Internal function (called from ObjectAnimator) to set up the setter and getter + * prior to running the animation. No getter can be used for multiple parameters. + * + * @param target The object on which the setter exists. + */ + @Override + void setupSetterAndGetter(Object target) { + setupSetter(target.getClass()); + } + + @Override + void setupSetter(Class targetClass) { + if (mJniSetter != 0) { + return; + } + try { + mPropertyMapLock.writeLock().lock(); + HashMap propertyMap = sJNISetterPropertyMap.get(targetClass); + if (propertyMap != null) { + Integer jniSetterInteger = propertyMap.get(mPropertyName); + if (jniSetterInteger != null) { + mJniSetter = jniSetterInteger; + } + } + if (mJniSetter == 0) { + String methodName = getMethodName("set", mPropertyName); + calculateValue(0f); + int[] values = (int[]) getAnimatedValue(); + int numParams = values.length; + try { + mJniSetter = nGetMultipleIntMethod(targetClass, methodName, numParams); + } catch (NoSuchMethodError e) { + // try without the 'set' prefix + mJniSetter = nGetMultipleIntMethod(targetClass, mPropertyName, numParams); + } + if (mJniSetter != 0) { + if (propertyMap == null) { + propertyMap = new HashMap(); + sJNISetterPropertyMap.put(targetClass, propertyMap); + } + propertyMap.put(mPropertyName, mJniSetter); + } + } + } finally { + mPropertyMapLock.writeLock().unlock(); + } + } + } + native static private int nGetIntMethod(Class targetClass, String methodName); native static private int nGetFloatMethod(Class targetClass, String methodName); + native static private int nGetMultipleIntMethod(Class targetClass, String methodName, + int numParams); + native static private int nGetMultipleFloatMethod(Class targetClass, String methodName, + int numParams); native static private void nCallIntMethod(Object target, int methodID, int arg); native static private void nCallFloatMethod(Object target, int methodID, float arg); + native static private void nCallTwoIntMethod(Object target, int methodID, int arg1, int arg2); + native static private void nCallFourIntMethod(Object target, int methodID, int arg1, int arg2, + int arg3, int arg4); + native static private void nCallMultipleIntMethod(Object target, int methodID, int[] args); + native static private void nCallTwoFloatMethod(Object target, int methodID, float arg1, + float arg2); + native static private void nCallFourFloatMethod(Object target, int methodID, float arg1, + float arg2, float arg3, float arg4); + native static private void nCallMultipleFloatMethod(Object target, int methodID, float[] args); } \ No newline at end of file diff --git a/core/jni/android_animation_PropertyValuesHolder.cpp b/core/jni/android_animation_PropertyValuesHolder.cpp index 59918054998f4..73347ea5226f6 100644 --- a/core/jni/android_animation_PropertyValuesHolder.cpp +++ b/core/jni/android_animation_PropertyValuesHolder.cpp @@ -47,6 +47,32 @@ static jmethodID android_animation_PropertyValuesHolder_getFloatMethod( return mid; } +static jmethodID getMultiparameterMethod(JNIEnv* env, jclass targetClass, jstring methodName, + jint parameterCount, char parameterType) +{ + const char *nativeString = env->GetStringUTFChars(methodName, 0); + char *signature = new char[parameterCount + 4]; + signature[0] = '('; + memset(&(signature[1]), parameterType, parameterCount); + strcpy(&(signature[parameterCount + 1]), ")V"); + jmethodID mid = env->GetMethodID(targetClass, nativeString, signature); + delete[] signature; + env->ReleaseStringUTFChars(methodName, nativeString); + return mid; +} + +static jmethodID android_animation_PropertyValuesHolder_getMultipleFloatMethod( + JNIEnv* env, jclass pvhClass, jclass targetClass, jstring methodName, jint parameterCount) +{ + return getMultiparameterMethod(env, targetClass, methodName, parameterCount, 'F'); +} + +static jmethodID android_animation_PropertyValuesHolder_getMultipleIntMethod( + JNIEnv* env, jclass pvhClass, jclass targetClass, jstring methodName, jint parameterCount) +{ + return getMultiparameterMethod(env, targetClass, methodName, parameterCount, 'I'); +} + static void android_animation_PropertyValuesHolder_callIntMethod( JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, int arg) { @@ -59,15 +85,85 @@ static void android_animation_PropertyValuesHolder_callFloatMethod( env->CallVoidMethod(target, methodID, arg); } +static void android_animation_PropertyValuesHolder_callTwoFloatMethod( + JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, float arg1, float arg2) +{ + env->CallVoidMethod(target, methodID, arg1, arg2); +} + +static void android_animation_PropertyValuesHolder_callFourFloatMethod( + JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, float arg1, float arg2, + float arg3, float arg4) +{ + env->CallVoidMethod(target, methodID, arg1, arg2, arg3, arg4); +} + +static void android_animation_PropertyValuesHolder_callMultipleFloatMethod( + JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, jfloatArray arg) +{ + jsize parameterCount = env->GetArrayLength(arg); + jfloat *floatValues = env->GetFloatArrayElements(arg, NULL); + jvalue* values = new jvalue[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + values[i].f = floatValues[i]; + } + env->CallVoidMethodA(target, methodID, values); + delete[] values; + env->ReleaseFloatArrayElements(arg, floatValues, JNI_ABORT); +} + +static void android_animation_PropertyValuesHolder_callTwoIntMethod( + JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, int arg1, int arg2) +{ + env->CallVoidMethod(target, methodID, arg1, arg2); +} + +static void android_animation_PropertyValuesHolder_callFourIntMethod( + JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, int arg1, int arg2, + int arg3, int arg4) +{ + env->CallVoidMethod(target, methodID, arg1, arg2, arg3, arg4); +} + +static void android_animation_PropertyValuesHolder_callMultipleIntMethod( + JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, jintArray arg) +{ + jsize parameterCount = env->GetArrayLength(arg); + jint *intValues = env->GetIntArrayElements(arg, NULL); + jvalue* values = new jvalue[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + values[i].i = intValues[i]; + } + env->CallVoidMethodA(target, methodID, values); + delete[] values; + env->ReleaseIntArrayElements(arg, intValues, JNI_ABORT); +} + static JNINativeMethod gMethods[] = { { "nGetIntMethod", "(Ljava/lang/Class;Ljava/lang/String;)I", (void*)android_animation_PropertyValuesHolder_getIntMethod }, { "nGetFloatMethod", "(Ljava/lang/Class;Ljava/lang/String;)I", (void*)android_animation_PropertyValuesHolder_getFloatMethod }, + { "nGetMultipleFloatMethod", "(Ljava/lang/Class;Ljava/lang/String;I)I", + (void*)android_animation_PropertyValuesHolder_getMultipleFloatMethod }, + { "nGetMultipleIntMethod", "(Ljava/lang/Class;Ljava/lang/String;I)I", + (void*)android_animation_PropertyValuesHolder_getMultipleIntMethod }, { "nCallIntMethod", "(Ljava/lang/Object;II)V", (void*)android_animation_PropertyValuesHolder_callIntMethod }, { "nCallFloatMethod", "(Ljava/lang/Object;IF)V", - (void*)android_animation_PropertyValuesHolder_callFloatMethod } + (void*)android_animation_PropertyValuesHolder_callFloatMethod }, + { "nCallTwoFloatMethod", "(Ljava/lang/Object;IFF)V", + (void*)android_animation_PropertyValuesHolder_callTwoFloatMethod }, + { "nCallFourFloatMethod", "(Ljava/lang/Object;IFFFF)V", + (void*)android_animation_PropertyValuesHolder_callFourFloatMethod }, + { "nCallMultipleFloatMethod", "(Ljava/lang/Object;I[F)V", + (void*)android_animation_PropertyValuesHolder_callMultipleFloatMethod }, + { "nCallTwoIntMethod", "(Ljava/lang/Object;III)V", + (void*)android_animation_PropertyValuesHolder_callTwoIntMethod }, + { "nCallFourIntMethod", "(Ljava/lang/Object;IIIII)V", + (void*)android_animation_PropertyValuesHolder_callFourIntMethod }, + { "nCallMultipleIntMethod", "(Ljava/lang/Object;I[I)V", + (void*)android_animation_PropertyValuesHolder_callMultipleIntMethod }, }; int register_android_animation_PropertyValuesHolder(JNIEnv* env)