package com.example.smart.ui.viewer;
|
|
import android.content.Context;
|
import android.content.res.AssetManager;
|
import android.content.res.Resources;
|
import android.content.res.TypedArray;
|
import android.graphics.Canvas;
|
import android.graphics.Paint;
|
import android.graphics.Rect;
|
import android.graphics.RectF;
|
import android.graphics.Typeface;
|
import android.graphics.drawable.Drawable;
|
import android.os.Handler;
|
import android.os.Message;
|
import android.text.TextUtils;
|
import android.util.AttributeSet;
|
import android.util.DisplayMetrics;
|
import android.util.LruCache;
|
import android.view.View;
|
|
import com.example.smart.R;
|
|
public class ProgressPieView extends View {
|
|
public interface OnProgressListener {
|
public void onProgressChanged(int progress, int max);
|
public void onProgressCompleted();
|
}
|
|
/**
|
* Fills the progress radially in a clockwise direction.
|
*/
|
public static final int FILL_TYPE_RADIAL = 0;
|
/**
|
* Fills the progress expanding from the center of the view.
|
*/
|
public static final int FILL_TYPE_CENTER = 1;
|
|
public static final int SLOW_ANIMATION_SPEED = 50;
|
public static final int MEDIUM_ANIMATION_SPEED = 25;
|
public static final int FAST_ANIMATION_SPEED = 1;
|
|
private static final int DEFAULT_MAX = 100;
|
private static final int DEFAULT_PROGRESS = 0;
|
private static final int DEFAULT_START_ANGLE = -90;
|
private static final float DEFAULT_STROKE_WIDTH = 3f;
|
private static final float DEFAULT_TEXT_SIZE = 14f;
|
private static final int DEFAULT_VIEW_SIZE = 96;
|
|
private static LruCache<String, Typeface> sTypefaceCache = new LruCache<String, Typeface>(8);
|
|
private OnProgressListener mListener;
|
private DisplayMetrics mDisplayMetrics;
|
private int mMax = DEFAULT_MAX;
|
private int mProgress = DEFAULT_PROGRESS;
|
private int mStartAngle = DEFAULT_START_ANGLE;
|
private boolean mInverted = false;
|
private boolean mCounterclockwise = false;
|
private boolean mShowStroke = true;
|
private float mStrokeWidth = DEFAULT_STROKE_WIDTH;
|
private boolean mShowText = true;
|
private float mTextSize = DEFAULT_TEXT_SIZE;
|
private String mText;
|
private String mTypeface;
|
private boolean mShowImage = true;
|
private Drawable mImage;
|
private Rect mImageRect;
|
private Paint mStrokePaint;
|
private Paint mTextPaint;
|
private Paint mProgressPaint;
|
private Paint mBackgroundPaint;
|
private RectF mInnerRectF;
|
private int mProgressFillType = FILL_TYPE_RADIAL;
|
|
private int mAnimationSpeed = MEDIUM_ANIMATION_SPEED;
|
private AnimationHandler mAnimationHandler = new AnimationHandler();
|
|
private int mViewSize;
|
|
public ProgressPieView(Context context) {
|
this(context, null);
|
}
|
|
public ProgressPieView(Context context, AttributeSet attrs) {
|
this(context, attrs, 0);
|
}
|
|
public ProgressPieView(Context context, AttributeSet attrs, int defStyleAttr) {
|
super(context, attrs, defStyleAttr);
|
init(context, attrs);
|
}
|
|
private void init(Context context, AttributeSet attrs) {
|
mDisplayMetrics = context.getResources().getDisplayMetrics();
|
|
mStrokeWidth = mStrokeWidth * mDisplayMetrics.density;
|
mTextSize = mTextSize * mDisplayMetrics.scaledDensity;
|
|
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressPieView);
|
final Resources res = getResources();
|
|
mMax = a.getInteger(R.styleable.ProgressPieView_ppvMax, mMax);
|
mProgress = a.getInteger(R.styleable.ProgressPieView_ppvProgress, mProgress);
|
mStartAngle = a.getInt(R.styleable.ProgressPieView_ppvStartAngle, mStartAngle);
|
mInverted = a.getBoolean(R.styleable.ProgressPieView_ppvInverted, mInverted);
|
mCounterclockwise = a.getBoolean(R.styleable.ProgressPieView_ppvCounterclockwise, mCounterclockwise);
|
mStrokeWidth = a.getDimension(R.styleable.ProgressPieView_ppvStrokeWidth, mStrokeWidth);
|
mTypeface = a.getString(R.styleable.ProgressPieView_ppvTypeface);
|
mTextSize = a.getDimension(R.styleable.ProgressPieView_android_textSize, mTextSize);
|
mText = a.getString(R.styleable.ProgressPieView_android_text);
|
|
mShowStroke = a.getBoolean(R.styleable.ProgressPieView_ppvShowStroke, mShowStroke);
|
mShowText = a.getBoolean(R.styleable.ProgressPieView_ppvShowText, mShowText);
|
mImage = a.getDrawable(R.styleable.ProgressPieView_ppvImage);
|
|
int backgroundColor = res.getColor(R.color.default_background_color);
|
backgroundColor = a.getColor(R.styleable.ProgressPieView_ppvBackgroundColor, backgroundColor);
|
int progressColor = res.getColor(R.color.default_progress_color);
|
progressColor = a.getColor(R.styleable.ProgressPieView_ppvProgressColor, progressColor);
|
int strokeColor = res.getColor(R.color.default_stroke_color);
|
strokeColor = a.getColor(R.styleable.ProgressPieView_ppvStrokeColor, strokeColor);
|
int textColor = res.getColor(R.color.default_text_color);
|
textColor = a.getColor(R.styleable.ProgressPieView_android_textColor, textColor);
|
|
mProgressFillType = a.getInteger(R.styleable.ProgressPieView_ppvProgressFillType, mProgressFillType);
|
|
a.recycle();
|
|
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
mBackgroundPaint.setColor(backgroundColor);
|
mBackgroundPaint.setStyle(Paint.Style.FILL);
|
|
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
mProgressPaint.setColor(progressColor);
|
mProgressPaint.setStyle(Paint.Style.FILL);
|
|
mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
mStrokePaint.setColor(strokeColor);
|
mStrokePaint.setStyle(Paint.Style.STROKE);
|
mStrokePaint.setStrokeWidth(mStrokeWidth);
|
|
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
mTextPaint.setColor(textColor);
|
mTextPaint.setTextSize(mTextSize);
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
|
mInnerRectF = new RectF();
|
mImageRect = new Rect();
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
int width = resolveSize(DEFAULT_VIEW_SIZE, widthMeasureSpec);
|
int height = resolveSize(DEFAULT_VIEW_SIZE, heightMeasureSpec);
|
mViewSize = Math.min(width, height);
|
|
setMeasuredDimension(width, height);
|
}
|
|
@Override
|
protected void onDraw(Canvas canvas) {
|
super.onDraw(canvas);
|
mInnerRectF.set(0, 0, mViewSize, mViewSize);
|
mInnerRectF.offset((getWidth() - mViewSize) / 2, (getHeight() - mViewSize) / 2);
|
if (mShowStroke) {
|
final int halfBorder = (int) (mStrokePaint.getStrokeWidth() / 2f + 0.5f);
|
mInnerRectF.inset(halfBorder, halfBorder);
|
}
|
float centerX = mInnerRectF.centerX();
|
float centerY = mInnerRectF.centerY();
|
|
canvas.drawArc(mInnerRectF, 0, 360, true, mBackgroundPaint);
|
|
switch (mProgressFillType) {
|
case FILL_TYPE_RADIAL:
|
float sweepAngle = 360 * mProgress / mMax;
|
if (mInverted) {
|
sweepAngle = sweepAngle - 360;
|
}
|
if (mCounterclockwise) {
|
sweepAngle = -sweepAngle;
|
}
|
canvas.drawArc(mInnerRectF, mStartAngle, sweepAngle, true, mProgressPaint);
|
break;
|
case FILL_TYPE_CENTER:
|
float radius = (mViewSize / 2) * ((float) mProgress / mMax);
|
if (mShowStroke) {
|
radius = radius + 0.5f - mStrokePaint.getStrokeWidth();
|
}
|
canvas.drawCircle(centerX, centerY, radius, mProgressPaint);
|
break;
|
default:
|
throw new IllegalArgumentException("Invalid Progress Fill = " + mProgressFillType);
|
}
|
|
if (!TextUtils.isEmpty(mText) && mShowText) {
|
if (!TextUtils.isEmpty(mTypeface)) {
|
Typeface typeface = sTypefaceCache.get(mTypeface);
|
if (null == typeface && null != getResources()) {
|
AssetManager assets = getResources().getAssets();
|
if (null != assets) {
|
typeface = Typeface.createFromAsset(assets, mTypeface);
|
sTypefaceCache.put(mTypeface, typeface);
|
}
|
}
|
mTextPaint.setTypeface(typeface);
|
}
|
int xPos = (int) centerX;
|
int yPos = (int) (centerY - (mTextPaint.descent() + mTextPaint.ascent()) / 2);
|
canvas.drawText(mText, xPos, yPos, mTextPaint);
|
}
|
|
if (null != mImage && mShowImage) {
|
int drawableSize = mImage.getIntrinsicWidth();
|
mImageRect.set(0, 0, drawableSize, drawableSize);
|
mImageRect.offset((getWidth() - drawableSize) / 2, (getHeight() - drawableSize) / 2);
|
mImage.setBounds(mImageRect);
|
mImage.draw(canvas);
|
}
|
|
if (mShowStroke) {
|
canvas.drawOval(mInnerRectF, mStrokePaint);
|
}
|
|
}
|
|
/**
|
* Gets the maximum progress value.
|
*/
|
public int getMax() {
|
return mMax;
|
}
|
|
/**
|
* Sets the maximum progress value. Defaults to 100.
|
*/
|
public void setMax(int max) {
|
if (max <= 0 || max < mProgress) {
|
throw new IllegalArgumentException(
|
String.format("Max (%d) must be > 0 and >= %d", max, mProgress));
|
}
|
mMax = max;
|
invalidate();
|
}
|
|
/**
|
* Sets the animation speed used in the animateProgressFill method.
|
*/
|
public void setAnimationSpeed(int animationSpeed){
|
this.mAnimationSpeed = animationSpeed;
|
}
|
|
/**
|
* Returns the current animation speed used in animateProgressFill method.
|
*/
|
public int getAnimationSpeed(){
|
return this.mAnimationSpeed;
|
}
|
|
/**
|
* Animates a progress fill of the view, using a Handler.
|
*/
|
public void animateProgressFill() {
|
mAnimationHandler.removeMessages(0);
|
mAnimationHandler.setAnimateTo(mMax);
|
mAnimationHandler.sendEmptyMessage(0);
|
invalidate();
|
}
|
|
/**
|
* Animates a progress fill of the view, using a Handler.
|
* @param animateTo - the progress value the animation should stop at (0 - MAX)
|
*/
|
public void animateProgressFill(int animateTo) {
|
mAnimationHandler.removeMessages(0);
|
if (animateTo > mMax || animateTo < 0) {
|
throw new IllegalArgumentException(
|
String.format("Animation progress (%d) is greater than the max progress (%d) or lower than 0 ", animateTo, mMax));
|
}
|
mAnimationHandler.setAnimateTo(animateTo);
|
mAnimationHandler.sendEmptyMessage(0);
|
invalidate();
|
}
|
|
/**
|
* Stops the views animation.
|
*/
|
public void stopAnimating() {
|
mAnimationHandler.removeMessages(0);
|
mAnimationHandler.setAnimateTo(mProgress);
|
invalidate();
|
}
|
|
/**
|
* Gets the current progress from 0 to max.
|
*/
|
public int getProgress() {
|
return mProgress;
|
}
|
|
/**
|
* Sets the current progress (must be between 0 and max).
|
*/
|
public void setProgress(int progress) {
|
if (progress > mMax || progress < 0) {
|
throw new IllegalArgumentException(
|
String.format("Progress (%d) must be between %d and %d", progress, 0, mMax));
|
}
|
mProgress = progress;
|
if (null != mListener) {
|
if (mProgress == mMax) {
|
mListener.onProgressCompleted();
|
} else {
|
mListener.onProgressChanged(mProgress, mMax);
|
}
|
}
|
invalidate();
|
}
|
|
/**
|
* Gets the start angle the {@link #FILL_TYPE_RADIAL} uses.
|
*/
|
public int getStartAngle() {
|
return mStartAngle;
|
}
|
|
/**
|
* Sets the start angle the {@link #FILL_TYPE_RADIAL} uses.
|
* @param startAngle start angle in degrees
|
*/
|
public void setStartAngle(int startAngle) {
|
mStartAngle = startAngle;
|
}
|
|
/**
|
* Gets the inverted state.
|
*/
|
public boolean isInverted() {
|
return mInverted;
|
}
|
|
/**
|
* Sets the inverted state.
|
* @param inverted draw the progress inverted or not
|
*/
|
public void setInverted(boolean inverted) {
|
mInverted = inverted;
|
}
|
|
/**
|
* Gets the counterclockwise state.
|
*/
|
public boolean isCounterclockwise() {
|
return mCounterclockwise;
|
}
|
|
/**
|
* Sets the counterclockwise state.
|
* @param counterclockwise draw the progress counterclockwise or not
|
*/
|
public void setCounterclockwise(boolean counterclockwise) {
|
mCounterclockwise = counterclockwise;
|
}
|
|
/**
|
* Gets the color used to display the progress of the view.
|
*/
|
public int getProgressColor() {
|
return mProgressPaint.getColor();
|
}
|
|
/**
|
* Sets the color used to display the progress of the view.
|
* @param color - color of the progress part of the view
|
*/
|
public void setProgressColor(int color) {
|
mProgressPaint.setColor(color);
|
invalidate();
|
}
|
|
/**
|
* Gets the color used to display the background of the view.
|
*/
|
public int getBackgroundColor() {
|
return mBackgroundPaint.getColor();
|
}
|
|
/**
|
* Sets the color used to display the background of the view.
|
* @param color - color of the background part of the view
|
*/
|
public void setBackgroundColor(int color) {
|
mBackgroundPaint.setColor(color);
|
invalidate();
|
}
|
|
/**
|
* Gets the color used to display the text of the view.
|
*/
|
public int getTextColor() {
|
return mTextPaint.getColor();
|
}
|
|
/**
|
* Sets the color used to display the text of the view.
|
* @param color - color of the text part of the view
|
*/
|
public void setTextColor(int color) {
|
mTextPaint.setColor(color);
|
invalidate();
|
}
|
|
/**
|
* Gets the text size in sp.
|
*/
|
public float getTextSize() {
|
return mTextSize;
|
}
|
|
/**
|
* Sets the text size.
|
* @param sizeSp in sp for the text
|
*/
|
public void setTextSize(int sizeSp) {
|
mTextSize = sizeSp * mDisplayMetrics.scaledDensity;
|
mTextPaint.setTextSize(mTextSize);
|
invalidate();
|
}
|
|
/**
|
* Gets the text of the view.
|
*/
|
public String getText() {
|
return mText;
|
}
|
|
/**
|
* Sets the text of the view.
|
* @param text to be displayed in the view
|
*/
|
public void setText(String text) {
|
mText = text;
|
invalidate();
|
}
|
|
/**
|
* Gets the typeface of the text.
|
*/
|
public String getTypeface() {
|
return mTypeface;
|
}
|
|
/**
|
* Sets the text typeface.
|
* - i.e. fonts/Roboto/Roboto-Regular.ttf
|
* @param typeface that the text is displayed in
|
*/
|
public void setTypeface(String typeface) {
|
mTypeface = typeface;
|
invalidate();
|
}
|
|
/**
|
* Gets the show text state.
|
*/
|
public boolean isTextShowing() {
|
return mShowText;
|
}
|
|
/**
|
* Sets the show text state.
|
* @param showText show or hide text
|
*/
|
public void setShowText(boolean showText) {
|
mShowText = showText;
|
invalidate();
|
}
|
|
/**
|
* Get the color used to display the stroke of the view.
|
*/
|
public int getStrokeColor() {
|
return mStrokePaint.getColor();
|
}
|
|
/**
|
* Sets the color used to display the stroke of the view.
|
* @param color - color of the stroke part of the view
|
*/
|
public void setStrokeColor(int color) {
|
mStrokePaint.setColor(color);
|
invalidate();
|
}
|
|
/**
|
* Gets the stroke width in dp.
|
*/
|
public float getStrokeWidth() {
|
return mStrokeWidth;
|
}
|
|
/**
|
* Set the stroke width.
|
* @param widthDp in dp for the pie border
|
*/
|
public void setStrokeWidth(int widthDp) {
|
mStrokeWidth = widthDp * mDisplayMetrics.density;
|
mStrokePaint.setStrokeWidth(mStrokeWidth);
|
invalidate();
|
}
|
|
/**
|
* Gets the show stroke state.
|
*/
|
public boolean isStrokeShowing() {
|
return mShowStroke;
|
}
|
|
/**
|
* Sets the show stroke state.
|
* @param showStroke show or hide stroke
|
*/
|
public void setShowStroke(boolean showStroke) {
|
mShowStroke = showStroke;
|
invalidate();
|
}
|
|
/**
|
* Gets the drawable of the view.
|
*/
|
public Drawable getImageDrawable() {
|
return mImage;
|
}
|
|
/**
|
* Sets the drawable of the view.
|
* @param image drawable of the view
|
*/
|
public void setImageDrawable(Drawable image) {
|
mImage = image;
|
invalidate();
|
}
|
|
/**
|
* Sets the drawable of the view.
|
* @param resId resource id of the view's drawable
|
*/
|
public void setImageResource(int resId) {
|
if (null != getResources()) {
|
mImage = getResources().getDrawable(resId);
|
invalidate();
|
}
|
}
|
|
/**
|
* Gets the show image state.
|
*/
|
public boolean isImageShowing() {
|
return mShowImage;
|
}
|
|
/**
|
* Sets the show image state.
|
* @param showImage show or hide image
|
*/
|
public void setShowImage(boolean showImage) {
|
mShowImage = showImage;
|
invalidate();
|
}
|
|
/**
|
* Gets the progress fill type.
|
*/
|
public int getProgressFillType() {
|
return mProgressFillType;
|
}
|
|
/**
|
* Sets the progress fill type.
|
* @param fillType one of {@link #FILL_TYPE_CENTER}, {@link #FILL_TYPE_RADIAL}
|
*/
|
public void setProgressFillType(int fillType) {
|
mProgressFillType = fillType;
|
}
|
|
/**
|
* Sets the progress listner.
|
* @param listener progress listener
|
*
|
* @see com.filippudak.ProgressPieView.ProgressPieView.OnProgressListener
|
*/
|
public void setOnProgressListener(OnProgressListener listener) {
|
mListener = listener;
|
}
|
|
/**
|
* Handler used to perform the fill animation.
|
*/
|
private class AnimationHandler extends Handler {
|
|
private int mAnimateTo;
|
|
public void setAnimateTo(int animateTo) {
|
mAnimateTo = animateTo;
|
}
|
|
@Override
|
public void handleMessage(Message msg) {
|
if (mProgress > mAnimateTo) {
|
setProgress(mProgress - 1);
|
sendEmptyMessageDelayed(0, mAnimationSpeed);
|
} else if (mProgress < mAnimateTo) {
|
setProgress(mProgress + 1);
|
sendEmptyMessageDelayed(0, mAnimationSpeed);
|
} else {
|
removeMessages(0);
|
}
|
}
|
}
|
|
}
|