Skip to content

Instantly share code, notes, and snippets.

@lzanita09
Last active January 20, 2016 17:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lzanita09/383141cfe500fb2b7348 to your computer and use it in GitHub Desktop.
Save lzanita09/383141cfe500fb2b7348 to your computer and use it in GitHub Desktop.
An animated Drawable that expands and reveals
package com.ifttt.iftttprototype;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.animation.Interpolator;
/**
* A custom {@link Drawable} that plays the Do Button special Recipe run animation.
* <p/>
* Currently due to the stroke rendering issue, on devices running lower than SDK 18, no stroke
* animation is run, instead an expand + fade out animation is used.
*/
public class CircleRevealDrawable extends Drawable {
private final Paint paint;
private final int startDiameter;
private final int maxDiameter;
private final int centerX;
private final int centerY;
private int currentRadius = -1;
private Interpolator interpolator;
public CircleRevealDrawable(int color, int startDiameter, int maxDiameter, int cx, int cy) {
this.startDiameter = startDiameter;
this.maxDiameter = maxDiameter;
centerX = cx;
centerY = cy;
paint = new Paint();
paint.setColor(color);
paint.setAntiAlias(true);
paint.setStyle(isAboveJellyBeanMR2() ? Paint.Style.STROKE : Paint.Style.FILL);
setBounds(centerX - startDiameter / 2, centerY - startDiameter / 2,
centerX + startDiameter / 2, centerY + startDiameter / 2);
currentRadius = startDiameter / 2;
}
public void setInterpolator(Interpolator interpolator) {
this.interpolator = interpolator;
}
@Override
public void draw(Canvas canvas) {
canvas.drawCircle(centerX, centerY, currentRadius, paint);
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return paint.getAlpha();
}
/**
* Start playing the expand and reveal animation.
*
* @param duration Duration of the animation.
* @param listener AnimatorListener instance for listening to the animation status.
*/
public void startAnimation(long duration, Animator.AnimatorListener listener) {
AnimatorSet set = isAboveJellyBeanMR2() ? buildAnimator(listener)
: buildAnimatorPreJellyBeanMR2(listener);
if (interpolator != null) {
set.setInterpolator(interpolator);
}
set.setDuration(duration);
set.start();
}
/**
* Build {@link AnimatorSet} for Jelly Bean MR2, KitKat and Lollipop with smooth
* stroke animation.
*
* @param listener Optional AnimatorListener
*/
private AnimatorSet buildAnimator(Animator.AnimatorListener listener) {
final int fullStroke = maxDiameter;
ValueAnimator expand = ValueAnimator.ofFloat(paint.getStrokeWidth(), fullStroke);
expand.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
paint.setStrokeWidth(animatedValue);
invalidateSelf();
}
});
ValueAnimator reveal = ValueAnimator.ofInt(startDiameter / 2, maxDiameter / 2);
reveal.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentRadius = (int) animation.getAnimatedValue();
paint.setStrokeWidth(fullStroke * (1 - animation.getAnimatedFraction()));
invalidateSelf();
}
});
AnimatorSet set = new AnimatorSet();
set.playSequentially(expand, reveal);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
currentRadius = startDiameter / 2;
paint.setStrokeWidth(1);
invalidateSelf();
}
});
if (listener != null) {
set.addListener(listener);
}
return set;
}
/**
* Build an {@link AnimatorSet} for pre-Jelly Bean MR2 devices, containing
* full circle expand and alpha animation.
*
* @param listener Optional AnimatorListener.
*/
private AnimatorSet buildAnimatorPreJellyBeanMR2(Animator.AnimatorListener listener) {
ValueAnimator expand = ValueAnimator.ofInt(startDiameter / 2, maxDiameter / 2);
expand.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
currentRadius = value;
invalidateSelf();
}
});
ValueAnimator alpha = ValueAnimator.ofInt(255, 0);
alpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
paint.setAlpha((int) animation.getAnimatedValue());
invalidateSelf();
}
});
AnimatorSet set = new AnimatorSet();
set.playSequentially(expand, alpha);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
currentRadius = 0;
paint.setAlpha(255);
invalidateSelf();
}
});
if (listener != null) {
set.addListener(listener);
}
return set;
}
private static boolean isAboveJellyBeanMR2() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment