package com.rey.material.drawable; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.os.SystemClock; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import com.rey.material.util.ViewUtil; /** * Created by Rey on 12/26/2014. */ public class CircleDrawable extends Drawable implements Animatable { private boolean mRunning = false; private long mStartTime; private float mAnimProgress; private int mAnimDuration = 1000; private Interpolator mInInterpolator = new DecelerateInterpolator(); private Interpolator mOutInterpolator = new DecelerateInterpolator(); private Paint mPaint; private float mX; private float mY; private float mRadius; private boolean mVisible; private boolean mInEditMode = false; private boolean mAnimEnable = true; public CircleDrawable() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); } public void setInEditMode(boolean b) { mInEditMode = b; } public void setAnimEnable(boolean b) { mAnimEnable = b; } public void setColor(int color) { mPaint.setColor(color); invalidateSelf(); } public void setAnimDuration(int duration) { mAnimDuration = duration; } public void setInterpolator(Interpolator in, Interpolator out) { mInInterpolator = in; mOutInterpolator = out; } @Override public boolean isStateful() { return true; } @Override protected boolean onStateChange(int[] state) { boolean visible = ViewUtil.hasState(state, android.R.attr.state_checked) || ViewUtil.hasState(state, android.R.attr.state_pressed); if (mVisible != visible) { mVisible = visible; if (!mInEditMode && mAnimEnable) start(); return true; } return false; } @Override protected void onBoundsChange(Rect bounds) { mX = bounds.exactCenterX(); mY = bounds.exactCenterY(); mRadius = Math.min(bounds.width(), bounds.height()) / 2f; } @Override public void draw(Canvas canvas) { if (!mRunning) { if (mVisible) canvas.drawCircle(mX, mY, mRadius, mPaint); } else { float radius = mVisible ? mInInterpolator.getInterpolation(mAnimProgress) * mRadius : (1f - mOutInterpolator.getInterpolation(mAnimProgress)) * mRadius; canvas.drawCircle(mX, mY, radius, mPaint); } } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } private void resetAnimation() { mStartTime = SystemClock.uptimeMillis(); mAnimProgress = 0f; } @Override public void start() { resetAnimation(); scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } @Override public void stop() { mRunning = false; unscheduleSelf(mUpdater); invalidateSelf(); } @Override public boolean isRunning() { return mRunning; } @Override public void scheduleSelf(Runnable what, long when) { mRunning = true; super.scheduleSelf(what, when); } private final Runnable mUpdater = new Runnable() { @Override public void run() { update(); } }; private void update() { long curTime = SystemClock.uptimeMillis(); mAnimProgress = Math.min(1f, (float) (curTime - mStartTime) / mAnimDuration); if (mAnimProgress == 1f) mRunning = false; if (isRunning()) scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } }