Skip to content

Instantly share code, notes, and snippets.

@swankjesse
Created February 8, 2016 01:37
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save swankjesse/8e3c61bd2f77760b02a8 to your computer and use it in GitHub Desktop.
Save swankjesse/8e3c61bd2f77760b02a8 to your computer and use it in GitHub Desktop.
/*
* Copyright (C) 2016 Jesse Wilson
*
* 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 com.publicobject.animation;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
/**
* A single floating point number that is potentially animating to a target floating point number.
* You can use this class instead of declaring a {@code float} field. You can {@linkplain #get get}
* and {@linkplain #set set} its value, similar to an {@link
* java.util.concurrent.atomic.AtomicInteger AtomicInteger}.
*
* <p>The real value of this class is when you use it to drive animations. Instead of calling
* {@link #set set()} to immediately set the value, call {@link #animateTo} to begin a transition
* from the current value to a new target value. It will use {@link ValueAnimator}'s timing pulse
* to acquire a new value on each animation frame. Use {@link #setInvalidatedView} to specify a
* view that will be invalided whenever the value changes. Typically this is a view that calls
* {@link #get} in {@link View#onDraw(Canvas) onDraw()}. Or subclass this class and override {@link
* #onAnimationUpdate onAnimationUpdate()} to configure other properties as the value of this
* number changes.
*
* <h3>Example</h3>
*
* Suppose we have a custom view that calls out to a drawable: <pre>{@code
*
* public void draw(Canvas canvas) {
* ...
*
* drawable.setBounds(left, top, right, bottom);
* drawable.draw(canvas);
*
* ...
* }
* }</pre>
*
* We can spin this drawable by using an {@code AnimatedFloat} to compute a rotation: <pre>{@code
*
* private final AnimatedFloat drawableRotation;
*
* public MyCustomView(Context context, AttributeSet attrs) {
* super(context, attrs);
*
* drawableRotation = new AnimatedFloat(0);
* drawableRotation.setInvalidatedView(this);
*
* ...
* }
*
* public void draw(Canvas canvas) {
* ...
*
* canvas.save();
* float rotation = drawableRotation.get();
* canvas.rotate(rotation, (left + right) / 2, (top + bottom) / 2);
* drawable.setBounds(left, top, right, bottom);
* drawable.draw(canvas);
* canvas.restore();
*
* ...
* }
* }</pre>
*
* To rotate the drawable, set the target value of the animated float. This will spin it in a
* circle: <pre>{@code
*
* drawableRotation.animateTo(drawableRotation.get() + 360);
* }</pre>
*/
public class AnimatedFloat implements ValueAnimator.AnimatorUpdateListener {
public static final TimeInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
public static final TimeInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
public static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private final ValueAnimator animator;
private View invalidatedView;
private int target;
public AnimatedFloat(int initialValue) {
this.animator = new ValueAnimator();
this.animator.setInterpolator(DECELERATE_INTERPOLATOR);
this.animator.addUpdateListener(this);
set(initialValue);
}
public final ValueAnimator animator() {
return animator;
}
public final void setInvalidatedView(View view) {
this.invalidatedView = view;
}
public final void set(int value) {
target = value;
animator.cancel();
animator.setFloatValues((float) value, (float) value);
animator.start();
}
public final void animateTo(int value) {
target = value;
animator.cancel();
animator.setFloatValues((Float) animator.getAnimatedValue(), (float) value);
animator.start();
}
public final float get() {
return (Float) animator.getAnimatedValue();
}
public final int target() {
return target;
}
@Override public void onAnimationUpdate(ValueAnimator animation) {
if (invalidatedView != null) {
invalidatedView.invalidate();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment