Last active
August 18, 2016 00:46
-
-
Save fnk0/b1f276e32c1051eda929f1acd092095c to your computer and use it in GitHub Desktop.
Circular progress bar for Android
This file contains hidden or 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
<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> |
This file contains hidden or 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
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