forked from RaghavSood/AndroidCircularSeekBar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CircularSeekBar.java
575 lines (488 loc) · 13.1 KB
/
CircularSeekBar.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
/**
* @author Raghav Sood
* @version 1
* @date 26 January, 2013
*/
package com.appaholics.circularseekbar;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* The Class CircularSeekBar.
*/
public class CircularSeekBar extends View {
/** The context */
private Context mContext;
/** The listener to listen for changes */
private OnSeekChangeListener mListener;
/** The color of the progress ring */
private Paint circleColor;
/** the color of the inside circle. Acts as background color */
private Paint innerColor;
/** The progress circle ring background */
private Paint circleRing;
/** The angle of progress */
private int angle = 0;
/** The start angle (12 O'clock */
private int startAngle = 270;
/** The width of the progress ring */
private int barWidth = 5;
/** The width of the view */
private int width;
/** The height of the view */
private int height;
/** The maximum progress amount */
private int maxProgress = 100;
/** The current progress */
private int progress;
/** The progress percent */
private int progressPercent;
/** The radius of the inner circle */
private float innerRadius;
/** The radius of the outer circle */
private float outerRadius;
/** The circle's center X coordinate */
private float cx;
/** The circle's center Y coordinate */
private float cy;
/** The left bound for the circle RectF */
private float left;
/** The right bound for the circle RectF */
private float right;
/** The top bound for the circle RectF */
private float top;
/** The bottom bound for the circle RectF */
private float bottom;
/** The X coordinate for the top left corner of the marking drawable */
private float dx;
/** The Y coordinate for the top left corner of the marking drawable */
private float dy;
/** The X coordinate for 12 O'Clock */
private float startPointX;
/** The Y coordinate for 12 O'Clock */
private float startPointY;
/**
* The X coordinate for the current position of the marker, pre adjustment
* to center
*/
private float markPointX;
/**
* The Y coordinate for the current position of the marker, pre adjustment
* to center
*/
private float markPointY;
/**
* The adjustment factor. This adds an adjustment of the specified size to
* both sides of the progress bar, allowing touch events to be processed
* more user friendlily (yes, I know that's not a word)
*/
private float adjustmentFactor = 100;
/** The progress mark when the view isn't being progress modified */
private Bitmap progressMark;
/** The progress mark when the view is being progress modified. */
private Bitmap progressMarkPressed;
/** The flag to see if view is pressed */
private boolean IS_PRESSED = false;
/**
* The flag to see if the setProgress() method was called from our own
* View's setAngle() method, or externally by a user.
*/
private boolean CALLED_FROM_ANGLE = false;
/** The rectangle containing our circles and arcs. */
private RectF rect = new RectF();
{
mListener = new OnSeekChangeListener() {
@Override
public void onProgressChange(CircularSeekBar view, int newProgress) {
}
};
circleColor = new Paint();
innerColor = new Paint();
circleRing = new Paint();
circleColor.setColor(Color.parseColor("#ff33b5e5")); // Set default
// progress
// color to holo
// blue.
innerColor.setColor(Color.BLACK); // Set default background color to
// black
circleRing.setColor(Color.GRAY);// Set default background color to Gray
circleColor.setAntiAlias(true);
innerColor.setAntiAlias(true);
circleRing.setAntiAlias(true);
circleColor.setStrokeWidth(5);
innerColor.setStrokeWidth(5);
circleRing.setStrokeWidth(5);
circleColor.setStyle(Paint.Style.FILL);
}
/**
* Instantiates a new circular seek bar.
*
* @param context
* the context
* @param attrs
* the attrs
* @param defStyle
* the def style
*/
public CircularSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
initDrawable();
}
/**
* Instantiates a new circular seek bar.
*
* @param context
* the context
* @param attrs
* the attrs
*/
public CircularSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initDrawable();
}
/**
* Instantiates a new circular seek bar.
*
* @param context
* the context
*/
public CircularSeekBar(Context context) {
super(context);
mContext = context;
initDrawable();
}
/**
* Inits the drawable.
*/
public void initDrawable() {
progressMark = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.scrubber_control_normal_holo);
progressMarkPressed = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.scrubber_control_pressed_holo);
}
/*
* (non-Javadoc)
*
* @see android.view.View#onMeasure(int, int)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth(); // Get View Width
height = getHeight();// Get View Height
int size = (width > height) ? height : width; // Choose the smaller
// between width and
// height to make a
// square
cx = width / 2; // Center X for circle
cy = height / 2; // Center Y for circle
outerRadius = size / 2; // Radius of the outer circle
innerRadius = outerRadius - barWidth; // Radius of the inner circle
left = cx - outerRadius; // Calculate left bound of our rect
right = cx + outerRadius;// Calculate right bound of our rect
top = cy - outerRadius;// Calculate top bound of our rect
bottom = cy + outerRadius;// Calculate bottom bound of our rect
startPointX = cx; // 12 O'clock X coordinate
startPointY = cy - outerRadius;// 12 O'clock Y coordinate
markPointX = startPointX;// Initial locatino of the marker X coordinate
markPointY = startPointY;// Initial locatino of the marker Y coordinate
rect.set(left, top, right, bottom); // assign size to rect
}
/*
* (non-Javadoc)
*
* @see android.view.View#onDraw(android.graphics.Canvas)
*/
@Override
protected void onDraw(Canvas canvas) {
dx = getXFromAngle();
dy = getYFromAngle();
canvas.drawCircle(cx, cy, outerRadius, circleRing);
canvas.drawArc(rect, startAngle, angle, true, circleColor);
canvas.drawCircle(cx, cy, innerRadius, innerColor);
drawMarkerAtProgress(canvas);
super.onDraw(canvas);
}
/**
* Draw marker at the current progress point onto the given canvas.
*
* @param canvas
* the canvas
*/
public void drawMarkerAtProgress(Canvas canvas) {
if (IS_PRESSED) {
canvas.drawBitmap(progressMarkPressed, dx, dy, null);
} else {
canvas.drawBitmap(progressMark, dx, dy, null);
}
}
/**
* Gets the X coordinate of the arc's end arm's point of intersection with
* the circle
*
* @return the X coordinate
*/
public float getXFromAngle() {
int size1 = progressMark.getWidth();
int size2 = progressMarkPressed.getWidth();
int adjust = (size1 > size2) ? size1 : size2;
float x = markPointX - (adjust / 2);
return x;
}
/**
* Gets the Y coordinate of the arc's end arm's point of intersection with
* the circle
*
* @return the Y coordinate
*/
public float getYFromAngle() {
int size1 = progressMark.getHeight();
int size2 = progressMarkPressed.getHeight();
int adjust = (size1 > size2) ? size1 : size2;
float y = markPointY - (adjust / 2);
return y;
}
/**
* Get the angle.
*
* @return the angle
*/
public int getAngle() {
return angle;
}
/**
* Set the angle.
*
* @param angle
* the new angle
*/
public void setAngle(int angle) {
this.angle = angle;
float donePercent = (((float) this.angle) / 360) * 100;
float progress = (donePercent / 100) * getMaxProgress();
setProgressPercent(Math.round(donePercent));
CALLED_FROM_ANGLE = true;
setProgress(Math.round(progress));
}
/**
* Sets the seek bar change listener.
*
* @param listener
* the new seek bar change listener
*/
public void setSeekBarChangeListener(OnSeekChangeListener listener) {
mListener = listener;
}
/**
* Gets the seek bar change listener.
*
* @return the seek bar change listener
*/
public OnSeekChangeListener getSeekBarChangeListener() {
return mListener;
}
/**
* Gets the bar width.
*
* @return the bar width
*/
public int getBarWidth() {
return barWidth;
}
/**
* Sets the bar width.
*
* @param barWidth
* the new bar width
*/
public void setBarWidth(int barWidth) {
this.barWidth = barWidth;
}
/**
* The listener interface for receiving onSeekChange events. The class that
* is interested in processing a onSeekChange event implements this
* interface, and the object created with that class is registered with a
* component using the component's
* <code>setSeekBarChangeListener(OnSeekChangeListener)<code> method. When
* the onSeekChange event occurs, that object's appropriate
* method is invoked.
*
* @see OnSeekChangeEvent
*/
public interface OnSeekChangeListener {
/**
* On progress change.
*
* @param view
* the view
* @param newProgress
* the new progress
*/
public void onProgressChange(CircularSeekBar view, int newProgress);
}
/**
* Gets the max progress.
*
* @return the max progress
*/
public int getMaxProgress() {
return maxProgress;
}
/**
* Sets the max progress.
*
* @param maxProgress
* the new max progress
*/
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
/**
* Gets the progress.
*
* @return the progress
*/
public int getProgress() {
return progress;
}
/**
* Sets the progress.
*
* @param progress
* the new progress
*/
public void setProgress(int progress) {
if (this.progress != progress) {
this.progress = progress;
if (!CALLED_FROM_ANGLE) {
int newPercent = (this.progress / this.maxProgress) * 100;
int newAngle = (newPercent / 100) * 360;
this.setAngle(newAngle);
this.setProgressPercent(newPercent);
}
mListener.onProgressChange(this, this.getProgress());
CALLED_FROM_ANGLE = false;
}
}
/**
* Gets the progress percent.
*
* @return the progress percent
*/
public int getProgressPercent() {
return progressPercent;
}
/**
* Sets the progress percent.
*
* @param progressPercent
* the new progress percent
*/
public void setProgressPercent(int progressPercent) {
this.progressPercent = progressPercent;
}
/**
* Sets the ring background color.
*
* @param color
* the new ring background color
*/
public void setRingBackgroundColor(int color) {
circleRing.setColor(color);
}
/**
* Sets the back ground color.
*
* @param color
* the new back ground color
*/
public void setBackGroundColor(int color) {
innerColor.setColor(color);
}
/**
* Sets the progress color.
*
* @param color
* the new progress color
*/
public void setProgressColor(int color) {
circleColor.setColor(color);
}
/*
* (non-Javadoc)
*
* @see android.view.View#onTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
boolean up = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
moved(x, y, up);
break;
case MotionEvent.ACTION_MOVE:
moved(x, y, up);
break;
case MotionEvent.ACTION_UP:
up = true;
moved(x, y, up);
break;
}
return true;
}
/**
* Moved.
*
* @param x
* the x
* @param y
* the y
* @param up
* the up
*/
private void moved(float x, float y, boolean up) {
float distance = (float) Math.sqrt(Math.pow((x - cx), 2) + Math.pow((y - cy), 2));
if (distance < outerRadius + adjustmentFactor && distance > innerRadius - adjustmentFactor && !up) {
IS_PRESSED = true;
markPointX = (float) (cx + outerRadius * Math.cos(Math.atan2(x - cx, cy - y) - (Math.PI /2)));
markPointY = (float) (cy + outerRadius * Math.sin(Math.atan2(x - cx, cy - y) - (Math.PI /2)));
float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(x - cx, cy - y)) + 360.0)) % 360.0);
// and to make it count 0-360
if (degrees < 0) {
degrees += 2 * Math.PI;
}
setAngle(Math.round(degrees));
invalidate();
} else {
IS_PRESSED = false;
invalidate();
}
}
/**
* Gets the adjustment factor.
*
* @return the adjustment factor
*/
public float getAdjustmentFactor() {
return adjustmentFactor;
}
/**
* Sets the adjustment factor.
*
* @param adjustmentFactor
* the new adjustment factor
*/
public void setAdjustmentFactor(float adjustmentFactor) {
this.adjustmentFactor = adjustmentFactor;
}
}