Skip to content

Commit 65e3c9c

Browse files
committed
1. Added progress button.
2. Releasing new version.
1 parent c6071a5 commit 65e3c9c

File tree

10 files changed

+1385
-14
lines changed

10 files changed

+1385
-14
lines changed
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
package com.amit.drawables;
2+
3+
import android.animation.Animator;
4+
import android.animation.AnimatorListenerAdapter;
5+
import android.animation.AnimatorSet;
6+
import android.animation.ValueAnimator;
7+
import android.graphics.Canvas;
8+
import android.graphics.ColorFilter;
9+
import android.graphics.Paint;
10+
import android.graphics.PixelFormat;
11+
import android.graphics.Rect;
12+
import android.graphics.RectF;
13+
import android.graphics.drawable.Animatable;
14+
import android.graphics.drawable.Drawable;
15+
import android.view.View;
16+
import android.view.animation.AccelerateDecelerateInterpolator;
17+
import android.view.animation.Interpolator;
18+
import android.view.animation.LinearInterpolator;
19+
20+
public class CircularAnimDrawable extends Drawable implements Animatable
21+
{
22+
public static final int MIN_PROGRESS = 0;
23+
public static final int MAX_PROGRESS = 100;
24+
25+
private AnimatorSet mAnimatorSet;
26+
private ValueAnimator mValueAnimatorAngle;
27+
private ValueAnimator mValueAnimatorSweep;
28+
private ValueAnimator mValueAnimatorProgress;
29+
30+
private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator();
31+
private static final Interpolator SWEEP_INTERPOLATOR = new AccelerateDecelerateInterpolator();
32+
33+
private static final Float MIN_SWEEP_ANGLE = 50f;
34+
private static final int SWEEP_ANIMATOR_DURATION = 700;
35+
private static final int ANGLE_ANIMATOR_DURATION = 2000;
36+
private static final int PROGRESS_ANIMATOR_DURATION = 200;
37+
38+
private Paint mPaint;
39+
private View mAnimatedView;
40+
private final RectF fBounds = new RectF();
41+
42+
private float mBorderWidth;
43+
private float mCurrentSweepAngle;
44+
private float mCurrentGlobalAngle;
45+
private float mCurrentGlobalAngleOffset;
46+
47+
private boolean mRunning;
48+
private boolean shouldDraw;
49+
private boolean mModeAppearing;
50+
51+
private int progress;
52+
private float shownProgress;
53+
54+
/**
55+
*
56+
* @param view View to be animated
57+
* @param borderWidth The width of the spinning bar
58+
* @param arcColor The color of the spinning bar
59+
*/
60+
public CircularAnimDrawable(View view, float borderWidth, int arcColor)
61+
{
62+
mAnimatedView = view;
63+
mBorderWidth = borderWidth;
64+
65+
mPaint = new Paint();
66+
mPaint.setAntiAlias(true);
67+
mPaint.setStyle(Paint.Style.STROKE);
68+
mPaint.setStrokeWidth(borderWidth);
69+
mPaint.setColor(arcColor);
70+
71+
setupAnimations();
72+
shouldDraw = true;
73+
mAnimatorSet = new AnimatorSet();
74+
}
75+
76+
@Override
77+
protected void onBoundsChange(Rect bounds)
78+
{
79+
super.onBoundsChange(bounds);
80+
81+
fBounds.left = bounds.left + mBorderWidth / 2f + .5f;
82+
fBounds.right = bounds.right - mBorderWidth / 2f - .5f;
83+
fBounds.top = bounds.top + mBorderWidth / 2f + .5f;
84+
fBounds.bottom = bounds.bottom - mBorderWidth / 2f - .5f;
85+
}
86+
87+
public void setLoadingBarColor(int color)
88+
{
89+
mPaint.setColor(color);
90+
}
91+
92+
/**
93+
* Start the animation
94+
*/
95+
@Override
96+
public void start()
97+
{
98+
if (isRunning())
99+
{
100+
return;
101+
}
102+
103+
mRunning = true;
104+
mAnimatorSet.playTogether(mValueAnimatorAngle, mValueAnimatorSweep);
105+
mAnimatorSet.start();
106+
107+
if (mValueAnimatorProgress != null && !mValueAnimatorProgress.isRunning())
108+
{
109+
mValueAnimatorProgress.start();
110+
}
111+
}
112+
113+
/**
114+
* Stops the animation
115+
*/
116+
@Override
117+
public void stop()
118+
{
119+
if (!isRunning())
120+
{
121+
return;
122+
}
123+
124+
mRunning = false;
125+
mAnimatorSet.cancel();
126+
}
127+
128+
/**
129+
* Method the inform if the animation is in process
130+
*
131+
* @return
132+
*/
133+
@Override
134+
public boolean isRunning()
135+
{
136+
return mRunning;
137+
}
138+
139+
/**
140+
* Method called when the drawable is going to draw itself.
141+
* @param canvas
142+
*/
143+
@Override
144+
public void draw(Canvas canvas)
145+
{
146+
float startAngle = mCurrentGlobalAngle - mCurrentGlobalAngleOffset;
147+
float sweepAngle = mCurrentSweepAngle;
148+
149+
if (progress >= MIN_PROGRESS && progress <= MAX_PROGRESS)
150+
{
151+
startAngle = -90;
152+
sweepAngle = shownProgress;
153+
}
154+
else if (!mModeAppearing)
155+
{
156+
startAngle = startAngle + sweepAngle;
157+
sweepAngle = 360 - sweepAngle - MIN_SWEEP_ANGLE;
158+
}
159+
else
160+
{
161+
sweepAngle += MIN_SWEEP_ANGLE;
162+
}
163+
164+
canvas.drawArc(fBounds, startAngle, sweepAngle, false, mPaint);
165+
}
166+
167+
@Override
168+
public void setAlpha(int alpha)
169+
{
170+
mPaint.setAlpha(alpha);
171+
}
172+
173+
@Override
174+
public void setColorFilter(ColorFilter colorFilter)
175+
{
176+
mPaint.setColorFilter(colorFilter);
177+
}
178+
179+
@Override
180+
public int getOpacity()
181+
{
182+
return PixelFormat.TRANSPARENT;
183+
}
184+
185+
/**
186+
* Set up all the animations. There are two animation: Global angle animation and sweep animation.
187+
*/
188+
private void setupAnimations()
189+
{
190+
mValueAnimatorAngle = ValueAnimator.ofFloat(0, 360f);
191+
mValueAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
192+
mValueAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
193+
mValueAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);
194+
195+
mValueAnimatorAngle.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
196+
{
197+
@Override
198+
public void onAnimationUpdate(ValueAnimator animation)
199+
{
200+
mCurrentGlobalAngle = (float)animation.getAnimatedValue();
201+
}
202+
});
203+
204+
mValueAnimatorSweep = ValueAnimator.ofFloat(0, 360f - 2 * MIN_SWEEP_ANGLE);
205+
mValueAnimatorSweep.setInterpolator(SWEEP_INTERPOLATOR);
206+
mValueAnimatorSweep.setDuration(SWEEP_ANIMATOR_DURATION);
207+
mValueAnimatorSweep.setRepeatCount(ValueAnimator.INFINITE);
208+
209+
mValueAnimatorSweep.addListener(new AnimatorListenerAdapter()
210+
{
211+
@Override
212+
public void onAnimationRepeat(Animator animation)
213+
{
214+
toggleAppearingMode();
215+
shouldDraw = false;
216+
}
217+
});
218+
219+
mValueAnimatorSweep.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
220+
{
221+
@Override
222+
public void onAnimationUpdate(ValueAnimator animation)
223+
{
224+
mCurrentSweepAngle = (float) animation.getAnimatedValue();
225+
226+
if (mCurrentSweepAngle < 5)
227+
{
228+
shouldDraw = true;
229+
}
230+
231+
if (shouldDraw)
232+
{
233+
mAnimatedView.invalidate();
234+
}
235+
}
236+
});
237+
238+
// progress animation is set up on each change of progress val
239+
}
240+
241+
/**
242+
* This method is called in every repetition of the animation, so the animation make the sweep
243+
* growing and then make it shirinking.
244+
*/
245+
private void toggleAppearingMode()
246+
{
247+
mModeAppearing = !mModeAppearing;
248+
249+
if (mModeAppearing)
250+
{
251+
mCurrentGlobalAngleOffset = (mCurrentGlobalAngleOffset + MIN_SWEEP_ANGLE * 2) % 360;
252+
}
253+
}
254+
255+
public void dispose()
256+
{
257+
if (mValueAnimatorAngle != null)
258+
{
259+
mValueAnimatorAngle.end();
260+
mValueAnimatorAngle.removeAllUpdateListeners();
261+
mValueAnimatorAngle.cancel();
262+
}
263+
264+
mValueAnimatorAngle = null;
265+
266+
if (mValueAnimatorSweep != null)
267+
{
268+
mValueAnimatorSweep.end();
269+
mValueAnimatorSweep.removeAllUpdateListeners();
270+
mValueAnimatorSweep.cancel();
271+
}
272+
273+
mValueAnimatorSweep = null;
274+
275+
if (mValueAnimatorProgress != null)
276+
{
277+
if (mValueAnimatorProgress.isRunning())
278+
{
279+
mValueAnimatorProgress.end();
280+
}
281+
282+
mValueAnimatorProgress.removeAllUpdateListeners();
283+
mValueAnimatorProgress.cancel();
284+
}
285+
286+
if (mAnimatorSet != null)
287+
{
288+
mAnimatorSet.end();
289+
mAnimatorSet.cancel();
290+
}
291+
}
292+
293+
public void setProgress(int progress)
294+
{
295+
if (this.progress == progress)
296+
{
297+
return;
298+
}
299+
300+
this.progress = progress;
301+
302+
if (progress < MIN_PROGRESS)
303+
{
304+
shownProgress = 0;
305+
}
306+
307+
if (mValueAnimatorProgress == null)
308+
{
309+
mValueAnimatorProgress = ValueAnimator.ofFloat(shownProgress, progress * 3.6f);
310+
mValueAnimatorProgress.setInterpolator(SWEEP_INTERPOLATOR);
311+
mValueAnimatorProgress.setDuration(PROGRESS_ANIMATOR_DURATION);
312+
313+
mValueAnimatorProgress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
314+
{
315+
@Override
316+
public void onAnimationUpdate(ValueAnimator animation)
317+
{
318+
shownProgress = (float) animation.getAnimatedValue();
319+
mAnimatedView.invalidate();
320+
}
321+
});
322+
}
323+
else
324+
{
325+
if (mValueAnimatorProgress.isRunning())
326+
{
327+
mValueAnimatorProgress.cancel();
328+
}
329+
330+
mValueAnimatorProgress.setFloatValues(shownProgress, progress * 3.6f);
331+
}
332+
333+
if (isRunning() && progress >= MIN_PROGRESS)
334+
{
335+
mValueAnimatorProgress.start();
336+
}
337+
}
338+
}

0 commit comments

Comments
 (0)