AnimatorInflaterpublic class AnimatorInflater extends Object This class is used to instantiate animator XML files into Animator objects.
For performance reasons, inflation relies heavily on pre-processing of
XML files that is done at build time. Therefore, it is not currently possible
to use this inflater with an XmlPullParser over a plain XML file at runtime;
it only works with an XmlPullParser returned from a compiled resource (R.
something file.) |
Fields Summary |
---|
private static final String | TAG | private static final int | TOGETHERThese flags are used when parsing AnimatorSet objects | private static final int | SEQUENTIALLY | private static final int | VALUE_TYPE_FLOATEnum values used in XML attributes to indicate the value for mValueType | private static final int | VALUE_TYPE_INT | private static final int | VALUE_TYPE_PATH | private static final int | VALUE_TYPE_COLOR | private static final int | VALUE_TYPE_CUSTOM | private static final boolean | DBG_ANIMATOR_INFLATER | private static final android.util.TypedValue | sTmpTypedValue |
Methods Summary |
---|
private static Animator | createAnimatorFromXml(android.content.res.Resources res, android.content.res.Resources.Theme theme, org.xmlpull.v1.XmlPullParser parser, float pixelSize)
return createAnimatorFromXml(res, theme, parser, Xml.asAttributeSet(parser), null, 0,
pixelSize);
| private static Animator | createAnimatorFromXml(android.content.res.Resources res, android.content.res.Resources.Theme theme, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize)
Animator anim = null;
ArrayList<Animator> childAnims = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("objectAnimator")) {
anim = loadObjectAnimator(res, theme, attrs, pixelSize);
} else if (name.equals("animator")) {
anim = loadAnimator(res, theme, attrs, null, pixelSize);
} else if (name.equals("set")) {
anim = new AnimatorSet();
TypedArray a;
if (theme != null) {
a = theme.obtainStyledAttributes(attrs, R.styleable.AnimatorSet, 0, 0);
} else {
a = res.obtainAttributes(attrs, R.styleable.AnimatorSet);
}
anim.appendChangingConfigurations(a.getChangingConfigurations());
int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
pixelSize);
a.recycle();
} else {
throw new RuntimeException("Unknown animator name: " + parser.getName());
}
if (parent != null) {
if (childAnims == null) {
childAnims = new ArrayList<Animator>();
}
childAnims.add(anim);
}
}
if (parent != null && childAnims != null) {
Animator[] animsArray = new Animator[childAnims.size()];
int index = 0;
for (Animator a : childAnims) {
animsArray[index++] = a;
}
if (sequenceOrdering == TOGETHER) {
parent.playTogether(animsArray);
} else {
parent.playSequentially(animsArray);
}
}
return anim;
| private static StateListAnimator | createStateListAnimatorFromXml(android.content.Context context, org.xmlpull.v1.XmlPullParser parser, android.util.AttributeSet attributeSet)
int type;
StateListAnimator stateListAnimator = new StateListAnimator();
while (true) {
type = parser.next();
switch (type) {
case XmlPullParser.END_DOCUMENT:
case XmlPullParser.END_TAG:
return stateListAnimator;
case XmlPullParser.START_TAG:
// parse item
Animator animator = null;
if ("item".equals(parser.getName())) {
int attributeCount = parser.getAttributeCount();
int[] states = new int[attributeCount];
int stateIndex = 0;
for (int i = 0; i < attributeCount; i++) {
int attrName = attributeSet.getAttributeNameResource(i);
if (attrName == R.attr.animation) {
final int animId = attributeSet.getAttributeResourceValue(i, 0);
animator = loadAnimator(context, animId);
} else {
states[stateIndex++] =
attributeSet.getAttributeBooleanValue(i, false) ?
attrName : -attrName;
}
}
if (animator == null) {
animator = createAnimatorFromXml(context.getResources(),
context.getTheme(), parser, 1f);
}
if (animator == null) {
throw new Resources.NotFoundException(
"animation state item must have a valid animation");
}
stateListAnimator
.addState(StateSet.trimStateSet(states, stateIndex), animator);
}
break;
}
}
| private static int | getChangingConfigs(android.content.res.Resources resources, int id)
synchronized (sTmpTypedValue) {
resources.getValue(id, sTmpTypedValue, true);
return sTmpTypedValue.changingConfigurations;
}
| public static Animator | loadAnimator(android.content.Context context, int id)Loads an {@link Animator} object from a resource
return loadAnimator(context.getResources(), context.getTheme(), id);
| private static ValueAnimator | loadAnimator(android.content.res.Resources res, android.content.res.Resources.Theme theme, android.util.AttributeSet attrs, ValueAnimator anim, float pathErrorScale)Creates a new animation whose parameters come from the specified context
and attributes set.
TypedArray arrayAnimator = null;
TypedArray arrayObjectAnimator = null;
if (theme != null) {
arrayAnimator = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
} else {
arrayAnimator = res.obtainAttributes(attrs, R.styleable.Animator);
}
// If anim is not null, then it is an object animator.
if (anim != null) {
if (theme != null) {
arrayObjectAnimator = theme.obtainStyledAttributes(attrs,
R.styleable.PropertyAnimator, 0, 0);
} else {
arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
}
anim.appendChangingConfigurations(arrayObjectAnimator.getChangingConfigurations());
}
if (anim == null) {
anim = new ValueAnimator();
}
anim.appendChangingConfigurations(arrayAnimator.getChangingConfigurations());
parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale);
final int resID = arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
if (resID > 0) {
final Interpolator interpolator = AnimationUtils.loadInterpolator(res, theme, resID);
if (interpolator instanceof BaseInterpolator) {
anim.appendChangingConfigurations(
((BaseInterpolator) interpolator).getChangingConfiguration());
}
anim.setInterpolator(interpolator);
}
arrayAnimator.recycle();
if (arrayObjectAnimator != null) {
arrayObjectAnimator.recycle();
}
return anim;
| public static Animator | loadAnimator(android.content.res.Resources resources, android.content.res.Resources.Theme theme, int id)Loads an {@link Animator} object from a resource
return loadAnimator(resources, theme, id, 1);
| public static Animator | loadAnimator(android.content.res.Resources resources, android.content.res.Resources.Theme theme, int id, float pathErrorScale)
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
.getAnimatorCache();
Animator animator = animatorCache.get(id, theme);
if (animator != null) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
}
return animator;
} else if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "cache miss for animator " + resources.getResourceName(id));
}
XmlResourceParser parser = null;
try {
parser = resources.getAnimation(id);
animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
if (animator != null) {
animator.appendChangingConfigurations(getChangingConfigs(resources, id));
final ConstantState<Animator> constantState = animator.createConstantState();
if (constantState != null) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "caching animator for res " + resources.getResourceName(id));
}
animatorCache.put(id, theme, constantState);
// create a new animator so that cached version is never used by the user
animator = constantState.newInstance(resources, theme);
}
}
return animator;
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} catch (IOException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} finally {
if (parser != null) parser.close();
}
| private static ObjectAnimator | loadObjectAnimator(android.content.res.Resources res, android.content.res.Resources.Theme theme, android.util.AttributeSet attrs, float pathErrorScale)
ObjectAnimator anim = new ObjectAnimator();
loadAnimator(res, theme, attrs, anim, pathErrorScale);
return anim;
| public static StateListAnimator | loadStateListAnimator(android.content.Context context, int id)
final Resources resources = context.getResources();
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
.getStateListAnimatorCache();
final Theme theme = context.getTheme();
StateListAnimator animator = cache.get(id, theme);
if (animator != null) {
return animator;
}
XmlResourceParser parser = null;
try {
parser = resources.getAnimation(id);
animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser));
if (animator != null) {
animator.appendChangingConfigurations(getChangingConfigs(resources, id));
final ConstantState<StateListAnimator> constantState = animator
.createConstantState();
if (constantState != null) {
cache.put(id, theme, constantState);
// return a clone so that the animator in constant state is never used.
animator = constantState.newInstance(resources, theme);
}
}
return animator;
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException(
"Can't load state list animator resource ID #0x" +
Integer.toHexString(id)
);
rnf.initCause(ex);
throw rnf;
} catch (IOException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException(
"Can't load state list animator resource ID #0x" +
Integer.toHexString(id)
);
rnf.initCause(ex);
throw rnf;
} finally {
if (parser != null) {
parser.close();
}
}
| private static void | parseAnimatorFromTypeArray(ValueAnimator anim, android.content.res.TypedArray arrayAnimator, android.content.res.TypedArray arrayObjectAnimator, float pixelSize)
long duration = arrayAnimator.getInt(R.styleable.Animator_duration, 300);
long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType,
VALUE_TYPE_FLOAT);
TypeEvaluator evaluator = null;
boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
TypedValue tvFrom = arrayAnimator.peekValue(R.styleable.Animator_valueFrom);
boolean hasFrom = (tvFrom != null);
int fromType = hasFrom ? tvFrom.type : 0;
TypedValue tvTo = arrayAnimator.peekValue(R.styleable.Animator_valueTo);
boolean hasTo = (tvTo != null);
int toType = hasTo ? tvTo.type : 0;
// TODO: Further clean up this part of code into 4 types : path, color,
// integer and float.
if (valueType == VALUE_TYPE_PATH) {
evaluator = setupAnimatorForPath(anim, arrayAnimator);
} else {
// Integer and float value types are handled here.
if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
(hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
// special case for colors: ignore valueType and get ints
getFloats = false;
evaluator = ArgbEvaluator.getInstance();
}
setupValues(anim, arrayAnimator, getFloats, hasFrom, fromType, hasTo, toType);
}
anim.setDuration(duration);
anim.setStartDelay(startDelay);
if (arrayAnimator.hasValue(R.styleable.Animator_repeatCount)) {
anim.setRepeatCount(
arrayAnimator.getInt(R.styleable.Animator_repeatCount, 0));
}
if (arrayAnimator.hasValue(R.styleable.Animator_repeatMode)) {
anim.setRepeatMode(
arrayAnimator.getInt(R.styleable.Animator_repeatMode,
ValueAnimator.RESTART));
}
if (evaluator != null) {
anim.setEvaluator(evaluator);
}
if (arrayObjectAnimator != null) {
setupObjectAnimator(anim, arrayObjectAnimator, getFloats, pixelSize);
}
| private static TypeEvaluator | setupAnimatorForPath(ValueAnimator anim, android.content.res.TypedArray arrayAnimator)Setup the Animator to achieve path morphing.
TypeEvaluator evaluator = null;
String fromString = arrayAnimator.getString(R.styleable.Animator_valueFrom);
String toString = arrayAnimator.getString(R.styleable.Animator_valueTo);
PathParser.PathDataNode[] nodesFrom = PathParser.createNodesFromPathData(fromString);
PathParser.PathDataNode[] nodesTo = PathParser.createNodesFromPathData(toString);
if (nodesFrom != null) {
if (nodesTo != null) {
anim.setObjectValues(nodesFrom, nodesTo);
if (!PathParser.canMorph(nodesFrom, nodesTo)) {
throw new InflateException(arrayAnimator.getPositionDescription()
+ " Can't morph from " + fromString + " to " + toString);
}
} else {
anim.setObjectValues((Object)nodesFrom);
}
evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesFrom));
} else if (nodesTo != null) {
anim.setObjectValues((Object)nodesTo);
evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesTo));
}
if (DBG_ANIMATOR_INFLATER && evaluator != null) {
Log.v(TAG, "create a new PathDataEvaluator here");
}
return evaluator;
| private static void | setupObjectAnimator(ValueAnimator anim, android.content.res.TypedArray arrayObjectAnimator, boolean getFloats, float pixelSize)Setup ObjectAnimator's property or values from pathData.
ObjectAnimator oa = (ObjectAnimator) anim;
String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
// Note that if there is a pathData defined in the Object Animator,
// valueFrom / valueTo will be ignored.
if (pathData != null) {
String propertyXName =
arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
String propertyYName =
arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyYName);
if (propertyXName == null && propertyYName == null) {
throw new InflateException(arrayObjectAnimator.getPositionDescription()
+ " propertyXName or propertyYName is needed for PathData");
} else {
Path path = PathParser.createPathFromPathData(pathData);
float error = 0.5f * pixelSize; // max half a pixel error
PathKeyframes keyframeSet = KeyframeSet.ofPath(path, error);
Keyframes xKeyframes;
Keyframes yKeyframes;
if (getFloats) {
xKeyframes = keyframeSet.createXFloatKeyframes();
yKeyframes = keyframeSet.createYFloatKeyframes();
} else {
xKeyframes = keyframeSet.createXIntKeyframes();
yKeyframes = keyframeSet.createYIntKeyframes();
}
PropertyValuesHolder x = null;
PropertyValuesHolder y = null;
if (propertyXName != null) {
x = PropertyValuesHolder.ofKeyframes(propertyXName, xKeyframes);
}
if (propertyYName != null) {
y = PropertyValuesHolder.ofKeyframes(propertyYName, yKeyframes);
}
if (x == null) {
oa.setValues(y);
} else if (y == null) {
oa.setValues(x);
} else {
oa.setValues(x, y);
}
}
} else {
String propertyName =
arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyName);
oa.setPropertyName(propertyName);
}
| private static void | setupValues(ValueAnimator anim, android.content.res.TypedArray arrayAnimator, boolean getFloats, boolean hasFrom, int fromType, boolean hasTo, int toType)Setup ValueAnimator's values.
This will handle all of the integer, float and color types.
int valueFromIndex = R.styleable.Animator_valueFrom;
int valueToIndex = R.styleable.Animator_valueTo;
if (getFloats) {
float valueFrom;
float valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
valueFrom = arrayAnimator.getDimension(valueFromIndex, 0f);
} else {
valueFrom = arrayAnimator.getFloat(valueFromIndex, 0f);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
} else {
valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueFrom, valueTo);
} else {
anim.setFloatValues(valueFrom);
}
} else {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
} else {
valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueTo);
}
} else {
int valueFrom;
int valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
valueFrom = (int) arrayAnimator.getDimension(valueFromIndex, 0f);
} else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueFrom = arrayAnimator.getColor(valueFromIndex, 0);
} else {
valueFrom = arrayAnimator.getInt(valueFromIndex, 0);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueTo = arrayAnimator.getColor(valueToIndex, 0);
} else {
valueTo = arrayAnimator.getInt(valueToIndex, 0);
}
anim.setIntValues(valueFrom, valueTo);
} else {
anim.setIntValues(valueFrom);
}
} else {
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueTo = arrayAnimator.getColor(valueToIndex, 0);
} else {
valueTo = arrayAnimator.getInt(valueToIndex, 0);
}
anim.setIntValues(valueTo);
}
}
}
|
|