package com.linfaxin.transitionplayer.transitions;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
import android.animation.TypeEvaluator;
import android.transitions.everywhere.Transition;
import android.transitions.everywhere.TransitionValues;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* Created by linfaxin on 2015/8/9.
* Email: linlinfaxin@163.com
*/
public abstract class AbsChangeValue extends Transition {
protected String[] valueNames;
protected final String propertyPrefix;
private TypeEvaluator typeEvaluator;
public AbsChangeValue(String... valueNames) {
this(null, valueNames);
}
public AbsChangeValue(TypeEvaluator typeEvaluator, String... valueNames) {
setTypeEvaluator(typeEvaluator);
this.valueNames = valueNames;
propertyPrefix = "android:" + getClass().getSimpleName() + ":";
}
public void setTypeEvaluator(TypeEvaluator typeEvaluator) {
this.typeEvaluator = typeEvaluator;
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
for(String valueName : valueNames) {
transitionValues.values.put(propertyPrefix + valueName, getPropertyValue(transitionValues.view, valueName));
}
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
for(String valueName : valueNames) {
transitionValues.values.put(propertyPrefix + valueName, getPropertyValue(transitionValues.view, valueName));
}
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
AnimatorSet animatorSet = new AnimatorSet();
final View view = endValues.view;
for(String valueName : valueNames) {
Object startValue = startValues.values.get(propertyPrefix + valueName);
Object endValue = endValues.values.get(propertyPrefix + valueName);
if(startValue!=null && endValue!=null && startValue.getClass()!=endValue.getClass()) continue;
Class c = null;
if(startValue!=null) c= startValue.getClass();
else if(endValue!=null) c=endValue.getClass();
if(c==null) continue;
ObjectAnimator animator;
if(c==Float.class || c==float.class){
animator = ObjectAnimator.ofFloat(view, valueName, (Float) startValue, (Float) endValue);
animatorSet.playTogether(animator);
}else if(c==Integer.class || c==int.class){
animator = ObjectAnimator.ofInt(view, valueName, (Integer) startValue, (Integer) endValue);
animatorSet.playTogether(animator);
}else{
animator = ObjectAnimator.ofObject(view, valueName, typeEvaluator, startValue, endValue);
animatorSet.playTogether(animator);
}
setPropertyValue(view, valueName, startValue);
if(typeEvaluator!=null) animator.setEvaluator(typeEvaluator);
}
ArrayList<Animator> animators = animatorSet.getChildAnimations();
if(animators!=null && animators.size()>0){
if(animators.size()==1) return animators.get(0);
return animatorSet;
}
return null;
}
protected Object getPropertyValue(View view, String propertyName){
Class c = view.getClass();
while(c!=Object.class){
try {
String methodName = getMethodName("get", propertyName);
Method m = c.getDeclaredMethod(methodName);
m.setAccessible(true);
return m.invoke(view);
}catch (Exception ignore){
}
c = c.getSuperclass();
}
return null;
}
// We try several different types when searching for appropriate setter/getter functions.
// The caller may have supplied values in a type that does not match the setter/getter
// functions (such as the integers 0 and 1 to represent floating point values for alpha).
// Also, the use of generics in constructors means that we end up with the Object versions
// of primitive types (Float vs. float). But most likely, the setter/getter functions
// will take primitive types instead.
// So we supply an ordered array of other types to try before giving up.
private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
Double.class, Integer.class};
private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
Float.class, Double.class};
private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
Float.class, Integer.class};
protected void setPropertyValue(View view, String propertyName, Object value){
Class c = view.getClass();
Class valueType = value.getClass();
while(c!=Object.class){
String methodName = getMethodName("set", propertyName);
Class[] typeVariants;
if (valueType.equals(Float.class)) {
typeVariants = FLOAT_VARIANTS;
} else if (valueType.equals(Integer.class)) {
typeVariants = INTEGER_VARIANTS;
} else if (valueType.equals(Double.class)) {
typeVariants = DOUBLE_VARIANTS;
} else {
typeVariants = new Class[1];
typeVariants[0] = valueType;
}
for (Class typeVariant : typeVariants) {
try {
Method m = c.getMethod(methodName, typeVariant);
m.setAccessible(true);
m.invoke(view, value);
return;
} catch (Exception e) {
// Swallow the error and keep trying other variants
}
}
c = c.getSuperclass();
}
}
/**
* Utility method to derive a setter/getter method name from a property name, where the
* prefix is typically "set" or "get" and the first letter of the property name is
* capitalized.
*
* @param prefix The precursor to the method name, before the property name begins, typically
* "set" or "get".
* @param propertyName The name of the property that represents the bulk of the method name
* after the prefix. The first letter of this word will be capitalized in the resulting
* method name.
* @return String the property name converted to a method name according to the conventions
* specified above.
*/
static String getMethodName(String prefix, String propertyName) {
if (propertyName == null || propertyName.length() == 0) {
// shouldn't get here
return prefix;
}
char firstLetter = Character.toUpperCase(propertyName.charAt(0));
String theRest = propertyName.substring(1);
return prefix + firstLetter + theRest;
}
}