Created
May 7, 2014 18:57
-
-
Save derekbrameyer/15ba1c6c73a56bda11a9 to your computer and use it in GitHub Desktop.
CircularProgressView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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