Forked from castorflex/CircularProgressDrawable.java
Last active
April 16, 2017 22:04
-
-
Save QuadFlask/5c5ae625410a6f635c64 to your computer and use it in GitHub Desktop.
self color change by color list
This file contains 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
package com.example.android.sunshine.app; | |
import android.animation.Animator; | |
import android.animation.ObjectAnimator; | |
import android.animation.ValueAnimator; | |
import android.graphics.Canvas; | |
import android.graphics.ColorFilter; | |
import android.graphics.Paint; | |
import android.graphics.PixelFormat; | |
import android.graphics.Rect; | |
import android.graphics.RectF; | |
import android.graphics.drawable.Animatable; | |
import android.graphics.drawable.Drawable; | |
import android.util.Property; | |
import android.view.animation.DecelerateInterpolator; | |
import android.view.animation.Interpolator; | |
import android.view.animation.LinearInterpolator; | |
public class CircularProgressDrawable extends Drawable implements Animatable { | |
private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator(); | |
private static final Interpolator SWEEP_INTERPOLATOR = new DecelerateInterpolator(); | |
private static final int ANGLE_ANIMATOR_DURATION = 2000; | |
private static final int SWEEP_ANIMATOR_DURATION = 600; | |
private static final int MIN_SWEEP_ANGLE = 30; | |
private final RectF fBounds = new RectF(); | |
private ObjectAnimator mObjectAnimatorSweep; | |
private ObjectAnimator mObjectAnimatorAngle; | |
private boolean mModeAppearing; | |
private Paint mPaint; | |
private float mCurrentGlobalAngleOffset; | |
private float mCurrentGlobalAngle; | |
private float mCurrentSweepAngle; | |
private float mBorderWidth; | |
private boolean mRunning; | |
private int[] colorList = new int[]{ | |
0xff4444, | |
0xffbb33, | |
0x99cc00, | |
0x33b5e5, | |
0xaa66cc, | |
}; | |
public CircularProgressDrawable(int color, float borderWidth) { | |
mBorderWidth = borderWidth; | |
mPaint = new Paint(); | |
mPaint.setAntiAlias(true); | |
mPaint.setStyle(Paint.Style.STROKE); | |
mPaint.setStrokeWidth(borderWidth); | |
mPaint.setColor(color); | |
setupAnimations(); | |
} | |
@Override | |
public void draw(Canvas canvas) { | |
float startAngle = mCurrentGlobalAngle - mCurrentGlobalAngleOffset; | |
float sweepAngle = mCurrentSweepAngle; | |
if (!mModeAppearing) { | |
startAngle = startAngle + sweepAngle; | |
sweepAngle = 360 - sweepAngle - MIN_SWEEP_ANGLE; | |
} else { | |
sweepAngle += MIN_SWEEP_ANGLE; | |
} | |
canvas.drawArc(fBounds, startAngle, sweepAngle, false, mPaint); | |
mPaint.setColor(interpolateColor(mCurrentGlobalAngle / 360, colorList)); | |
} | |
private int interpolateColor(float colorStep, int[] colorList) { | |
int color1 = (int) (colorList.length * colorStep) % colorList.length; | |
int color2 = (int) (colorList.length * colorStep + 1) % colorList.length; | |
colorStep = colorStep * colorList.length - color1; | |
int r1 = colorList[color1] >> 16 & 0xff; | |
int g1 = colorList[color1] >> 8 & 0xff; | |
int b1 = colorList[color1] & 0xff; | |
int r2 = colorList[color2] >> 16 & 0xff; | |
int g2 = colorList[color2] >> 8 & 0xff; | |
int b2 = colorList[color2] & 0xff; | |
r1 += (r2 - r1) * colorStep; | |
g1 += (g2 - g1) * colorStep; | |
b1 += (b2 - b1) * colorStep; | |
return 0xff000000 | (r1 << 16 & 0xff0000) | (g1 << 8 & 0xff00) | (b1 & 0xff); | |
} | |
@Override | |
public void setAlpha(int alpha) { | |
mPaint.setAlpha(alpha); | |
} | |
@Override | |
public void setColorFilter(ColorFilter cf) { | |
mPaint.setColorFilter(cf); | |
} | |
@Override | |
public int getOpacity() { | |
return PixelFormat.TRANSPARENT; | |
} | |
private void toggleAppearingMode() { | |
mModeAppearing = !mModeAppearing; | |
if (mModeAppearing) { | |
mCurrentGlobalAngleOffset = (mCurrentGlobalAngleOffset + MIN_SWEEP_ANGLE * 2) % 360; | |
} | |
} | |
@Override | |
protected void onBoundsChange(Rect bounds) { | |
super.onBoundsChange(bounds); | |
fBounds.left = bounds.left + mBorderWidth / 2f + .5f; | |
fBounds.right = bounds.right - mBorderWidth / 2f - .5f; | |
fBounds.top = bounds.top + mBorderWidth / 2f + .5f; | |
fBounds.bottom = bounds.bottom - mBorderWidth / 2f - .5f; | |
} | |
////////////////////////////////////////////////////////////////////////////// | |
//////////////// Animation | |
private Property<CircularProgressDrawable, Float> mAngleProperty = new Property<CircularProgressDrawable, Float>(Float.class, "angle") { | |
@Override | |
public Float get(CircularProgressDrawable object) { | |
return object.getCurrentGlobalAngle(); | |
} | |
@Override | |
public void set(CircularProgressDrawable object, Float value) { | |
object.setCurrentGlobalAngle(value); | |
} | |
}; | |
private Property<CircularProgressDrawable, Float> mSweepProperty = new Property<CircularProgressDrawable, Float>(Float.class, "arc") { | |
@Override | |
public Float get(CircularProgressDrawable object) { | |
return object.getCurrentSweepAngle(); | |
} | |
@Override | |
public void set(CircularProgressDrawable object, Float value) { | |
object.setCurrentSweepAngle(value); | |
} | |
}; | |
private void setupAnimations() { | |
mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, mAngleProperty, 360f); | |
mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR); | |
mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION); | |
mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART); | |
mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE); | |
mObjectAnimatorSweep = ObjectAnimator.ofFloat(this, mSweepProperty, 360f - MIN_SWEEP_ANGLE * 2); | |
mObjectAnimatorSweep.setInterpolator(SWEEP_INTERPOLATOR); | |
mObjectAnimatorSweep.setDuration(SWEEP_ANIMATOR_DURATION); | |
mObjectAnimatorSweep.setRepeatMode(ValueAnimator.RESTART); | |
mObjectAnimatorSweep.setRepeatCount(ValueAnimator.INFINITE); | |
mObjectAnimatorSweep.addListener(new Animator.AnimatorListener() { | |
@Override | |
public void onAnimationStart(Animator animation) { | |
} | |
@Override | |
public void onAnimationEnd(Animator animation) { | |
} | |
@Override | |
public void onAnimationCancel(Animator animation) { | |
} | |
@Override | |
public void onAnimationRepeat(Animator animation) { | |
toggleAppearingMode(); | |
} | |
}); | |
} | |
@Override | |
public void start() { | |
if (isRunning()) { | |
return; | |
} | |
mRunning = true; | |
mObjectAnimatorAngle.start(); | |
mObjectAnimatorSweep.start(); | |
invalidateSelf(); | |
} | |
@Override | |
public void stop() { | |
if (!isRunning()) { | |
return; | |
} | |
mRunning = false; | |
mObjectAnimatorAngle.cancel(); | |
mObjectAnimatorSweep.cancel(); | |
invalidateSelf(); | |
} | |
@Override | |
public boolean isRunning() { | |
return mRunning; | |
} | |
public void setCurrentGlobalAngle(float currentGlobalAngle) { | |
mCurrentGlobalAngle = currentGlobalAngle; | |
invalidateSelf(); | |
} | |
public float getCurrentGlobalAngle() { | |
return mCurrentGlobalAngle; | |
} | |
public void setCurrentSweepAngle(float currentSweepAngle) { | |
mCurrentSweepAngle = currentSweepAngle; | |
invalidateSelf(); | |
} | |
public float getCurrentSweepAngle() { | |
return mCurrentSweepAngle; | |
} | |
} |
This file contains 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
/** | |
* Simplest custom view possible, using CircularProgressDrawable | |
*/ | |
public class CustomView extends View { | |
private CircularProgressDrawable mDrawable; | |
public CustomView(Context context) { | |
this(context, null); | |
} | |
public CustomView(Context context, AttributeSet attrs) { | |
this(context, attrs, 0); | |
} | |
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
mDrawable = new CircularProgressDrawable(Color.RED, 10); | |
mDrawable.setCallback(this); | |
} | |
@Override | |
protected void onVisibilityChanged(View changedView, int visibility) { | |
super.onVisibilityChanged(changedView, visibility); | |
if (visibility == VISIBLE) { | |
mDrawable.start(); | |
} else { | |
mDrawable.stop(); | |
} | |
} | |
@Override | |
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
super.onSizeChanged(w, h, oldw, oldh); | |
mDrawable.setBounds(0, 0, w, h); | |
} | |
@Override | |
public void draw(Canvas canvas) { | |
super.draw(canvas); | |
mDrawable.draw(canvas); | |
} | |
@Override | |
protected boolean verifyDrawable(Drawable who) { | |
return who == mDrawable || super.verifyDrawable(who); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment