Skip to content

Instantly share code, notes, and snippets.

@fnk0
Last active August 18, 2016 00:46
Show Gist options
  • Save fnk0/b1f276e32c1051eda929f1acd092095c to your computer and use it in GitHub Desktop.
Save fnk0/b1f276e32c1051eda929f1acd092095c to your computer and use it in GitHub Desktop.
Circular progress bar for Android
<declare-styleable name="CircularProgressBar">
<attr name="cpb_progress" format="integer" />
<attr name="cpb_progressbar_color" format="color" />
<attr name="cpb_background_progressbar_color" format="color" />
<attr name="cpb_progressbar_width" format="dimension" />
<attr name="cpb_background_progressbar_width" format="dimension" />
<attr name="cpb_rounded" format="boolean" />
</declare-styleable>
public class CircularProgressBar extends View {
private static final int DEFAULT_ANIMATION = 1500;
// Properties
private float progress = 0;
private float strokeWidth = getResources().getDimension(R.dimen.default_stroke_width);
private float backgroundStrokeWidth = getResources().getDimension(R.dimen.default_background_stroke_width);
private boolean isRounded = false;
private int color = Color.BLACK;
private int backgroundColor = Color.GRAY;
// Object used to draw
private int startAngle = -90;
private RectF rectF;
private Paint backgroundPaint;
private Paint foregroundPaint;
boolean refreshGradient = false;
SweepGradient mSweepGradient;
int[] mColors;
//region Constructor & Init Method
public CircularProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
rectF = new RectF();
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularProgressBar, 0, 0);
//Reading values from the XML layout
try {
// Value
progress = typedArray.getFloat(R.styleable.CircularProgressBar_cpb_progress, progress);
// StrokeWidth
strokeWidth = typedArray.getDimension(R.styleable.CircularProgressBar_cpb_progressbar_width, strokeWidth);
backgroundStrokeWidth = typedArray.getDimension(R.styleable.CircularProgressBar_cpb_background_progressbar_width, backgroundStrokeWidth);
// Color
color = typedArray.getInt(R.styleable.CircularProgressBar_cpb_progressbar_color, color);
backgroundColor = typedArray.getInt(R.styleable.CircularProgressBar_cpb_background_progressbar_color, backgroundColor);
} finally {
typedArray.recycle();
}
// Init Background
backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backgroundPaint.setColor(backgroundColor);
backgroundPaint.setStyle(Paint.Style.STROKE);
backgroundPaint.setStrokeWidth(backgroundStrokeWidth);
// Init Foreground
foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
foregroundPaint.setColor(color);
foregroundPaint.setStyle(Paint.Style.STROKE);
foregroundPaint.setStrokeWidth(strokeWidth);
foregroundPaint.setStrokeCap(Paint.Cap.ROUND);
}
//endregion
//region Draw Method
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawOval(rectF, backgroundPaint);
float angle = 360 * progress / 100;
if (mColors != null && mColors.length != 0 || refreshGradient) {
if (mSweepGradient == null) {
mSweepGradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight(), mColors, null);
}
refreshGradient = false;
}
if (mSweepGradient != null) {
foregroundPaint.setShader(mSweepGradient);
}
canvas.drawArc(rectF, startAngle, angle, false, foregroundPaint);
}
//endregion
//region Mesure Method
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
final int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int min = Math.min(width, height);
setMeasuredDimension(min, min);
float highStroke = (strokeWidth > backgroundStrokeWidth) ? strokeWidth : backgroundStrokeWidth;
rectF.set(0 + highStroke / 2, 0 + highStroke / 2, min - highStroke / 2, min - highStroke / 2);
}
//endregion
//region Method Get/Set
public float getProgress() {
return progress;
}
public CircularProgressBar setProgress(float progress) {
this.progress = (progress<=100) ? progress : 100;
invalidate();
return this;
}
public float getProgressBarWidth() {
return strokeWidth;
}
public CircularProgressBar setProgressBarWidth(float strokeWidth) {
this.strokeWidth = strokeWidth;
foregroundPaint.setStrokeWidth(strokeWidth);
refresh();
return this;
}
public float getBackgroundProgressBarWidth() {
return backgroundStrokeWidth;
}
public CircularProgressBar setBackgroundProgressBarWidth(float backgroundStrokeWidth) {
this.backgroundStrokeWidth = backgroundStrokeWidth;
backgroundPaint.setStrokeWidth(backgroundStrokeWidth);
refresh();
return this;
}
public int getColor() {
return color;
}
public CircularProgressBar setColor(int color) {
this.color = color;
foregroundPaint.setColor(color);
invalidate();
requestLayout();
return this;
}
public int getBackgroundColor() {
return backgroundColor;
}
public CircularProgressBar setProgressBackgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
backgroundPaint.setColor(backgroundColor);
refresh();
return this;
}
//endregion
public CircularProgressBar setGradientColors(int[] colors) {
mColors = new int[colors.length];
for(int i = 0; i < colors.length; i++) {
mColors[i] = getResources().getColor(colors[i]);
}
mSweepGradient = null;
refreshGradient = true;
refresh();
return this;
}
/**
* Where this CircularProgressBar has a rounded CAP or not
* @param rounded
* If is rounded or not
* @return
* This instance of CircularProgressBar
*/
public CircularProgressBar setRounded(boolean rounded) {
isRounded = rounded;
refresh();
return this;
}
/**
* Simple method to invalidate() and requestLayout() at the same time.
*/
private void refresh() {
invalidate();
requestLayout();
}
//region Other Method
/**
* Set the progress with an animation.
* Note that the {@link ObjectAnimator} Class automatically set the progress
* so don't call the {@link CircularProgressBar#setProgress(float)} directly within this method.
*
* @param progress The progress it should animate to it.
*/
public CircularProgressBar setProgressWithAnimation(float progress) {
setProgressWithAnimation(progress, DEFAULT_ANIMATION);
return this;
}
/**
* Set the progress with an animation.
* Note that the {@link ObjectAnimator} Class automatically set the progress
* so don't call the {@link CircularProgressBar#setProgress(float)} directly within this method.
*
* @param progress The progress it should animate to it.
* @param duration The length of the animation, in milliseconds.
*/
public CircularProgressBar setProgressWithAnimation(float progress, int duration) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "progress", progress);
objectAnimator.setDuration(duration);
objectAnimator.setInterpolator(new DecelerateInterpolator());
objectAnimator.start();
return this;
}
//endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment