/** @return An {@link ObjectKeyframeSet} with evenly distributed keyframe values. */ @NonNull public static <T> KeyframeSet<T> ofObject(ValueEvaluator<T> evaluator, T[] values) { final int numKeyframes = values.length; final List<Keyframe<T>> keyframes = new ArrayList<>(Math.max(numKeyframes, 2)); if (numKeyframes == 1) { keyframes.add(Keyframe.<T>of(0f)); keyframes.add(Keyframe.of(1f, values[0])); } else { keyframes.add(Keyframe.of(0f, values[0])); for (int i = 1; i < numKeyframes; i++) { keyframes.add(Keyframe.of((float) i / (numKeyframes - 1), values[i])); } } return new ObjectKeyframeSet<>(evaluator, keyframes); }
/** * Utility function to set fractions on keyframes to cover a gap in which the fractions are not * currently set. Keyframe fractions will be distributed evenly in this gap. For example, a gap of * 1 keyframe in the range 0-1 will be at .5, a gap of .6 spread between two keyframes will be at * .2 and .4 beyond the fraction at the keyframe before startIndex. Assumptions: - First and last * keyframe fractions (bounding this spread) are already set. So, for example, if no fractions are * set, we will already set first and last keyframe fraction values to 0 and 1. - startIndex must * be >0 (which follows from first assumption). - endIndex must be >= startIndex. * * @param keyframes the array of keyframes * @param gap The total gap we need to distribute * @param startIndex The index of the first keyframe whose fraction must be set * @param endIndex The index of the last keyframe whose fraction must be set */ private static void distributeKeyframes( Keyframe[] keyframes, float gap, int startIndex, int endIndex) { int count = endIndex - startIndex + 2; float increment = gap / count; for (int i = startIndex; i <= endIndex; ++i) { keyframes[i].fraction(keyframes[i - 1].getFraction() + increment); } }
public ObjectKeyframeSet(ValueEvaluator<T> evaluator, List<Keyframe<T>> keyframes) { this.evaluator = evaluator; this.numKeyframes = keyframes.size(); this.keyframes = keyframes; this.firstKf = keyframes.get(0); this.lastKf = keyframes.get(numKeyframes - 1); this.interpolator = lastKf.getInterpolator(); }
float endFraction = lastKeyframe.getFraction(); if (endFraction < 1) { if (endFraction < 0) { lastKeyframe.fraction(1); } else { keyframes.add(keyframes.size(), Keyframe.of(1)); count++; float startFraction = firstKeyframe.getFraction(); if (startFraction != 0) { if (startFraction < 0) { firstKeyframe.fraction(0); } else { keyframes.add(0, Keyframe.of(0)); count++; for (int i = 0; i < count; i++) { Keyframe keyframe = keyframeArray[i]; if (keyframe.getFraction() < 0) { if (i == 0) { keyframe.fraction(0); } else if (i == count - 1) { keyframe.fraction(1); } else { int endIndex = i; for (int j = startIndex + 1; j < count - 1; ++j) { if (keyframeArray[j].getFraction() >= 0) { break;
return evaluator.evaluate(fraction, firstKf.getValue(), lastKf.getValue()); final TimeInterpolator interpolator = nextKf.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); final float prevFraction = firstKf.getFraction(); final float intervalFraction = (fraction - prevFraction) / (nextKf.getFraction() - prevFraction); return evaluator.evaluate(intervalFraction, firstKf.getValue(), nextKf.getValue()); final TimeInterpolator interpolator = lastKf.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); final float prevFraction = prefKf.getFraction(); final float intervalFraction = (fraction - prevFraction) / (lastKf.getFraction() - prevFraction); return evaluator.evaluate(intervalFraction, prefKf.getValue(), lastKf.getValue()); if (fraction < nextKf.getFraction()) { final TimeInterpolator interpolator = nextKf.getInterpolator(); final float prevFraction = prevKf.getFraction(); float intervalFraction = (fraction - prevFraction) / (nextKf.getFraction() - prevFraction); return evaluator.evaluate(intervalFraction, prevKf.getValue(), nextKf.getValue()); return lastKf.getValue();
float value = TypedArrayUtils.getNamedFloat(a, parser, "value", Styleable.Keyframe.VALUE, 0); keyframe = Keyframe.of(fraction, value); break; case VALUE_TYPE_COLOR: int intValue = TypedArrayUtils.getNamedInt(a, parser, "value", Styleable.Keyframe.VALUE, 0); keyframe = Keyframe.of(fraction, intValue); break; keyframe = (valueType == VALUE_TYPE_FLOAT) ? Keyframe.of(fraction) : Keyframe.of(fraction); a, parser, "interpolator", Styleable.Keyframe.INTERPOLATOR, 0); if (resId > 0) { keyframe.interpolator(loadInterpolator(context, resId));
/** * Called when the animations are first initialized, so that the animation's keyframes can fill in * any missing start values. */ void setupStartValue(V startValue) { isInitialized = true; final List<Keyframe<T>> keyframes = keyframeSet.getKeyframes(); for (int i = 0, size = keyframes.size(); i < size; i++) { final Keyframe<T> kf = keyframes.get(i); if (kf.getValue() == null) { kf.value(transformBack(startValue)); } } }
@Override public int compare(Keyframe<?> k1, Keyframe<?> k2) { return Float.compare(k1.getFraction(), k2.getFraction()); } };
/** * Constructs a {@link Keyframe} object with the given time and value. The time defines the time, * as a proportion of an overall animation's duration, at which the value will hold true for the * animation. The value for the animation between keyframes will be calculated as an interpolation * between the values at those keyframes. * * @param <T> The keyframe value type. * @param fraction The time, expressed as a value between 0 and 1, representing the fraction of * time elapsed of the overall animation duration. * @param value The value that the object will animate to as the animation time approaches the * time in this {@link Keyframe}, and the the value animated from as the time passes the time * in this {@link Keyframe}. May be null. * @return The constructed {@link Keyframe} object. */ @NonNull public static <T> Keyframe<T> of( @FloatRange(from = 0f, to = 1f) float fraction, @Nullable T value) { return new Keyframe<>(fraction, value); }
/** @return An {@link ObjectKeyframeSet} with the given keyframe values. */ @NonNull public static <T> KeyframeSet<T> ofObject(ValueEvaluator<T> evaluator, Keyframe<T>[] values) { Arrays.sort(values, KEYFRAME_COMPARATOR); final List<Keyframe<T>> list = new ArrayList<>(values.length); final Set<Float> seenFractions = new HashSet<>(values.length); for (int i = values.length - 1; i >= 0; i--) { if (!seenFractions.contains(values[i].getFraction())) { list.add(values[i]); seenFractions.add(values[i].getFraction()); } } Collections.reverse(list); return new ObjectKeyframeSet<>(evaluator, list); }
/** * Constructs a {@link Keyframe} object with the given time. The value at this time will be * derived from the target object when the animation first starts. The time defines the time, as a * proportion of an overall animation's duration, at which the value will hold true for the * animation. The value for the animation between keyframes will be calculated as an interpolation * between the values at those keyframes. * * @param <T> The keyframe value type. * @param fraction The time, expressed as a value between 0 and 1, representing the fraction of * time elapsed of the overall animation duration. * @return The constructed {@link Keyframe} object. */ @NonNull public static <T> Keyframe<T> of(@FloatRange(from = 0f, to = 1f) float fraction) { return of(fraction, null); }