From 0161402ab423ab6df482357a053906b285658361 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 15 Jun 2014 23:36:58 +0300 Subject: [PATCH] Add ProgressWheel --- .../components/progressbar/ProgressWheel.java | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 src/com/todddavies/components/progressbar/ProgressWheel.java diff --git a/src/com/todddavies/components/progressbar/ProgressWheel.java b/src/com/todddavies/components/progressbar/ProgressWheel.java new file mode 100644 index 0000000..c676638 --- /dev/null +++ b/src/com/todddavies/components/progressbar/ProgressWheel.java @@ -0,0 +1,213 @@ +package com.todddavies.components.progressbar; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.os.Handler; +import android.os.Message; +import android.util.DisplayMetrics; +import android.view.View; + + +/** + * An indicator of progress, similar to Android's ProgressBar. + * Can be used in 'spin mode' or 'increment mode' + * + * @author Todd Davies + *

+ * Licensed under the Creative Commons Attribution 3.0 license see: + * http://creativecommons.org/licenses/by/3.0/ + */ +public class ProgressWheel extends View { + + //Colors (with defaults) + private final int barColor = 0xFF33b5e5; + private final int rimColor = 0x4433b5e5; + + //Paints + private final Paint barPaint, rimPaint, circlePaint; + + //Rectangles + private RectF circleBounds; + + //Animation + //The amount of pixels to move the bar by on each draw + private final int spinSpeed = 2; + + //Sizes + private final int fullRadius, barLength, barWidth, rimWidth; + + private int progress; + private boolean isSpinning; + + public ProgressWheel(Context context) { + super(context); + final DisplayMetrics dm = context.getResources().getDisplayMetrics(); + fullRadius = Math.round(50 * dm.density); + barLength = Math.round(25 * dm.density); + barWidth = Math.round(5 * dm.density); + rimWidth = Math.round(3 * dm.density); + + progress = 0; + isSpinning = false; + + barPaint = new Paint(); + rimPaint = new Paint(); + circlePaint = new Paint(); + circleBounds = new RectF(); + } + + /* + * When this is called, make the view square. + * From: http://www.jayway.com/2012/12/12/creating-custom-android-views-part-4-measuring-and-how-to-force-a-view-to-be-square/ + * + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // The first thing that happen is that we call the superclass + // implementation of onMeasure. The reason for that is that measuring + // can be quite a complex process and calling the super method is a + // convenient way to get most of this complexity handled. + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // We can’t use getWidth() or getHight() here. During the measuring + // pass the view has not gotten its final size yet (this happens first + // at the start of the layout pass) so we have to use getMeasuredWidth() + // and getMeasuredHeight(). + int size = 0; + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight(); + int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom(); + + // Finally we have some simple logic that calculates the size of the view + // and calls setMeasuredDimension() to set that size. + // Before we compare the width and height of the view, we remove the padding, + // and when we set the dimension we add it back again. Now the actual content + // of the view will be square, but, depending on the padding, the total dimensions + // of the view might not be. + if (widthWithoutPadding > heigthWithoutPadding) { + size = heigthWithoutPadding; + } else { + size = widthWithoutPadding; + } + + // If you override onMeasure() you have to call setMeasuredDimension(). + // This is how you report back the measured size. If you don’t call + // setMeasuredDimension() the parent will throw an exception and your + // application will crash. + // We are calling the onMeasure() method of the superclass so we don’t + // actually need to call setMeasuredDimension() since that takes care + // of that. However, the purpose with overriding onMeasure() was to + // change the default behaviour and to do that we need to call + // setMeasuredDimension() with our own values. + setMeasuredDimension(size + getPaddingLeft() + getPaddingRight(), size + getPaddingTop() + getPaddingBottom()); + } + + /** + * Use onSizeChanged instead of onAttachedToWindow to get the dimensions of the view, + * because this method is called after measuring the dimensions of MATCH_PARENT & WRAP_CONTENT. + * Use this dimensions to setup the bounds and paints. + */ + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + setupBounds(); + setupPaints(); + invalidate(); + } + + /** + * Set the properties of the paints we're using to + * draw the progress wheel + */ + private void setupPaints() { + barPaint.setColor(barColor); + barPaint.setAntiAlias(true); + barPaint.setStyle(Style.STROKE); + barPaint.setStrokeWidth(barWidth); + + rimPaint.setColor(rimColor); + rimPaint.setAntiAlias(true); + rimPaint.setStyle(Style.STROKE); + rimPaint.setStrokeWidth(rimWidth); + + circlePaint.setColor(0x44FFFFFF); + circlePaint.setStyle(Style.FILL); + } + + /** + * Set the bounds of the component + */ + private void setupBounds() { + final int centerX = getWidth() / 2; + final int centerY = getHeight() / 2; + circleBounds = new RectF(centerX - fullRadius, centerY - fullRadius, + centerX + fullRadius, centerY + fullRadius); + } + + //---------------------------------- + //Animation stuff + //---------------------------------- + + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + //Draw the inner circle + canvas.drawArc(circleBounds, 360, 360, false, circlePaint); + //Draw the rim + canvas.drawArc(circleBounds, 360, 360, false, rimPaint); + //Draw the bar + if (isSpinning) { + canvas.drawArc(circleBounds, progress - 90, barLength, false, + barPaint); + } else { + canvas.drawArc(circleBounds, -90, progress, false, barPaint); + } + } + + /** + * Turn off spin mode + */ + public void stopSpinning() { + isSpinning = false; + progress = 0; + spinHandler.removeMessages(0); + } + + /** + * Puts the view on spin mode + */ + public void spin() { + isSpinning = true; + spinHandler.sendEmptyMessage(0); + } + + /** + * Set the progress to a specific value + */ + public void setProgress(int i) { + isSpinning = false; + progress = i; + spinHandler.sendEmptyMessage(0); + } + + private final Handler spinHandler = new Handler() { + /** + * This is the code that will increment the progress variable + * and so spin the wheel + */ + @Override + public void handleMessage(Message msg) { + invalidate(); + if (isSpinning) { + progress += spinSpeed; + if (progress > 360) { + progress = 0; + } + spinHandler.sendEmptyMessage(0); + } + } + }; +}