Skip to content

Instantly share code, notes, and snippets.

@derekbrameyer
Created May 7, 2014 18:57
Show Gist options
  • Save derekbrameyer/15ba1c6c73a56bda11a9 to your computer and use it in GitHub Desktop.
Save derekbrameyer/15ba1c6c73a56bda11a9 to your computer and use it in GitHub Desktop.
CircularProgressView.java
package com.doomonafireball.samples.android.widget;
import com.doomonafireball.samples.android.R;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
/**
* User: derek Date: 5/6/14 Time: 10:50 AM
*/
public class CircularProgressView extends View {
private static final int STATE_NOT_STARTED = 0;
private static final int STATE_STARTED = 1;
private static final int STATE_PREPARE_FINISHING = 2;
private static final int STATE_FINISHING = 3;
private static final int STATE_FINISH_IMMEDIATELY = 4;
private int mState = STATE_NOT_STARTED;
private long mAnimationTime = 1000;
private long mStartTime;
private int mRadius = 50;
private int mStrokeWidth = 3;
private int mBackgroundColor;
private int mBackgroundColorPressed;
private int[] mProgressColors;
private int mCurrentColorIndex = 0;
private RectF mClipRect = new RectF();
private Paint mOldStrokePaint;
private Paint mNewStrokePaint;
private Paint mBackgroundPaint;
private Paint mBackgroundPaintPressed;
private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
private int mWidth;
public CircularProgressView(Context context) {
this(context, null, 0);
}
public CircularProgressView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularProgressView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
setClickable(true);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView, 0, 0);
mAnimationTime = (long) a.getFloat(R.styleable.CircularProgressView_cpv__animationTime, 750);
mRadius = a.getDimensionPixelSize(R.styleable.CircularProgressView_cpv__insideRadius, 250);
mStrokeWidth = a.getDimensionPixelSize(R.styleable.CircularProgressView_cpv__progressWidth, 20);
mBackgroundColor = a.getColor(R.styleable.CircularProgressView_cpv__backgroundColor, Color.BLACK);
mBackgroundColorPressed = a
.getColor(R.styleable.CircularProgressView_cpv__backgroundColorPressed, Color.DKGRAY);
a.recycle();
Resources res = context.getResources();
mProgressColors = new int[4];
mProgressColors[0] = res.getColor(R.color.holo_red_light);
mProgressColors[1] = res.getColor(R.color.holo_green_light);
mProgressColors[2] = res.getColor(R.color.holo_blue_bright);
mProgressColors[3] = res.getColor(R.color.holo_orange_light);
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(mBackgroundColor);
mBackgroundPaint.setStyle(Paint.Style.FILL);
mBackgroundPaint.setAntiAlias(true);
mBackgroundPaintPressed = new Paint();
mBackgroundPaintPressed.setColor(mBackgroundColorPressed);
mBackgroundPaintPressed.setStyle(Paint.Style.FILL);
mBackgroundPaintPressed.setAntiAlias(true);
mOldStrokePaint = new Paint();
mOldStrokePaint.setColor(Color.TRANSPARENT);
mOldStrokePaint.setStrokeWidth(mStrokeWidth);
mOldStrokePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mOldStrokePaint.setAntiAlias(true);
mNewStrokePaint = new Paint();
mNewStrokePaint.setColor(Color.TRANSPARENT);
mNewStrokePaint.setStrokeWidth(mStrokeWidth);
mNewStrokePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mNewStrokePaint.setAntiAlias(true);
mWidth = (mRadius * 2) + (mStrokeWidth * 2);
mClipRect = new RectF(mStrokeWidth / 2, mStrokeWidth / 2, mWidth - (mStrokeWidth / 2),
mWidth - (mStrokeWidth / 2));
}
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
}
public void setProgressColors(int[] progressColors) {
mProgressColors = progressColors;
}
public void start() {
// Start an animation
mStartTime = System.currentTimeMillis();
mState = STATE_STARTED;
mCurrentColorIndex = 0;
mOldStrokePaint.setColor(Color.TRANSPARENT);
mNewStrokePaint.setColor(mProgressColors[0]);
ViewCompat.postInvalidateOnAnimation(this);
}
public void finish() {
// Gracefully finish out the last animation
mState = STATE_PREPARE_FINISHING;
}
public void stop() {
// Immediately stop the animation and clears the ring
mState = STATE_FINISH_IMMEDIATELY;
mOldStrokePaint.setColor(Color.TRANSPARENT);
mNewStrokePaint.setColor(Color.TRANSPARENT);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
invalidate();
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mState == STATE_NOT_STARTED) {
drawForegroundCircle(canvas);
return;
} else if (mState == STATE_FINISH_IMMEDIATELY) {
canvas.drawArc(mClipRect, 0, 360f, true, mOldStrokePaint);
canvas.drawArc(mClipRect, 0, 360f, true, mNewStrokePaint);
mState = STATE_NOT_STARTED;
drawForegroundCircle(canvas);
return;
}
long elapsedTime = System.currentTimeMillis() - mStartTime;
float completionPercentage = (float) elapsedTime / (float) mAnimationTime;
completionPercentage = mInterpolator.getInterpolation(completionPercentage);
if (mState == STATE_STARTED || mState == STATE_PREPARE_FINISHING) {
canvas.drawArc(mClipRect, 0, 360f, false, mOldStrokePaint);
canvas.drawArc(mClipRect, -90, completionPercentage * 180f, true, mNewStrokePaint);
canvas.drawArc(mClipRect, -90, -completionPercentage * 180f, true, mNewStrokePaint);
} else if (mState == STATE_FINISHING) {
// Draw out the current color (now transparent), and "reverse" draw the color arc
canvas.drawArc(mClipRect, 0, 360f, false, mOldStrokePaint);
canvas.drawArc(mClipRect, 90, -1 * (180f - (completionPercentage * 180f)), true, mNewStrokePaint);
canvas.drawArc(mClipRect, 90, 180f - (completionPercentage * 180f), true, mNewStrokePaint);
}
if (elapsedTime > mAnimationTime) {
// We are advancing to the next color
if (mState == STATE_STARTED) {
mOldStrokePaint.setColor(mProgressColors[mCurrentColorIndex]);
mCurrentColorIndex += 1;
if (mCurrentColorIndex > (mProgressColors.length - 1)) {
mCurrentColorIndex = 0;
}
mNewStrokePaint.setColor(mProgressColors[mCurrentColorIndex]);
mStartTime = System.currentTimeMillis();
} else if (mState == STATE_PREPARE_FINISHING) {
mOldStrokePaint.setColor(Color.TRANSPARENT);
mNewStrokePaint.setColor(mProgressColors[mCurrentColorIndex]);
mStartTime = System.currentTimeMillis();
mState = STATE_FINISHING;
} else if (mState == STATE_FINISHING) {
// We are done drawing, set the paints so they are drawn as transparent once more
mState = STATE_NOT_STARTED;
mOldStrokePaint.setColor(Color.TRANSPARENT);
mNewStrokePaint.setColor(Color.TRANSPARENT);
}
}
drawForegroundCircle(canvas);
ViewCompat.postInvalidateOnAnimation(this);
}
private void drawForegroundCircle(Canvas canvas) {
// Draw the "background" (really the foreground) for this view
canvas.drawCircle(mWidth / 2, mWidth / 2, mRadius,
isPressed() ? mBackgroundPaintPressed : mBackgroundPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mWidth, mWidth);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment