package com.marshalchen.common.uimodule.customPullRefreshLayout.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; import android.util.TypedValue; import java.security.InvalidParameterException; /** * Created by baoyz on 14/10/31. */ class CirclesDrawable extends RefreshDrawable implements Runnable { private static final float MAX_LEVEL = 10000; private static final float CIRCLE_COUNT = ProgressStates.values().length; private static final float MAX_LEVEL_PER_CIRCLE = MAX_LEVEL / CIRCLE_COUNT; private static final int ALPHA_OPAQUE = 255; private Paint mFstHalfPaint; private Paint mScndHalfPaint; private Paint mAbovePaint; private RectF mOval = new RectF(); private int mDiameter; private Path mPath; private int mHalf; private ProgressStates mCurrentState; private int mControlPointMinimum; private int mControlPointMaximum; private int mAxisValue; private ColorFilter mColorFilter; private static int mColor1; private static int mColor2; private static int mColor3; private static int mColor4; private int fstColor, scndColor; private boolean goesBackward; private Handler mHandler = new Handler(); private int mLevel; private boolean isRunning; private int mTop; private int mDrawWidth; private int mDrawHeight; private Rect mBounds; public CirclesDrawable(Context context, PullRefreshLayout layout) { super(context, layout); } @Override public void start() { mLevel = 2500; isRunning = true; mHandler.postDelayed(this, 10); } @Override public void stop() { isRunning = false; mHandler.removeCallbacks(this); } @Override public boolean isRunning() { return isRunning; } @Override public void setColorSchemeColors(int[] colorSchemeColors) { initCirclesProgress(colorSchemeColors); } @Override public void setPercent(float percent) { int level = (int) (2500 * percent); updateLevel(level); } private void updateLevel(int level){ int animationLevel = level == MAX_LEVEL ? 0 : level; int stateForLevel = (int) (animationLevel / MAX_LEVEL_PER_CIRCLE); mCurrentState = ProgressStates.values()[stateForLevel]; resetColor(mCurrentState); int levelForCircle = (int) (animationLevel % MAX_LEVEL_PER_CIRCLE); boolean halfPassed; if (!goesBackward) { halfPassed = levelForCircle != (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2)); } else { halfPassed = levelForCircle == (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2)); levelForCircle = (int) (MAX_LEVEL_PER_CIRCLE - levelForCircle); } mFstHalfPaint.setColor(fstColor); mScndHalfPaint.setColor(scndColor); if (!halfPassed) { mAbovePaint.setColor(mScndHalfPaint.getColor()); } else { mAbovePaint.setColor(mFstHalfPaint.getColor()); } mAbovePaint.setAlpha(200 + (int) (55 * (levelForCircle / MAX_LEVEL_PER_CIRCLE))); mAxisValue = (int) (mControlPointMinimum + (mControlPointMaximum - mControlPointMinimum) * (levelForCircle / MAX_LEVEL_PER_CIRCLE)); } @Override public void offsetTopAndBottom(int offset) { mTop += offset; invalidateSelf(); } @Override public void run() { mLevel += 80; if (mLevel > MAX_LEVEL) mLevel = 0; if (isRunning) { mHandler.postDelayed(this, 20); updateLevel(mLevel); invalidateSelf(); } } private enum ProgressStates { FOLDING_DOWN, FOLDING_LEFT, FOLDING_UP, FOLDING_RIGHT } private void initCirclesProgress(int[] colors) { initColors(colors); mPath = new Path(); Paint basePaint = new Paint(); basePaint.setAntiAlias(true); mFstHalfPaint = new Paint(basePaint); mScndHalfPaint = new Paint(basePaint); mAbovePaint = new Paint(basePaint); setColorFilter(mColorFilter); } private void initColors(int[] colors) { if (colors == null || colors.length < 4) throw new InvalidParameterException("The color scheme length must be 4"); mColor1 = colors[0]; mColor2 = colors[1]; mColor3 = colors[2]; mColor4 = colors[3]; } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mDrawWidth = dp2px(40); mDrawHeight = mDrawWidth; mTop = -mDrawHeight - (getRefreshLayout().getFinalOffset() - mDrawHeight) / 2; mBounds = bounds; measureCircleProgress(mDrawWidth, mDrawHeight); } private void resetColor(ProgressStates currentState) { switch (currentState) { case FOLDING_DOWN: fstColor = mColor1; scndColor = mColor2; goesBackward = false; break; case FOLDING_LEFT: fstColor = mColor1; scndColor = mColor3; goesBackward = true; break; case FOLDING_UP: fstColor = mColor3; scndColor = mColor4; goesBackward = true; break; case FOLDING_RIGHT: fstColor = mColor2; scndColor = mColor4; goesBackward = false; break; } } @Override public void draw(Canvas canvas) { if (mCurrentState != null) { canvas.save(); canvas.translate(mBounds.width() / 2 - mDrawWidth / 2, mTop); makeCirclesProgress(canvas); canvas.restore(); } } private void measureCircleProgress(int width, int height) { mDiameter = Math.min(width, height); mHalf = mDiameter / 2; mOval.set(0, 0, mDiameter, mDiameter); mControlPointMinimum = -mDiameter / 6; mControlPointMaximum = mDiameter + mDiameter / 6; } private void makeCirclesProgress(Canvas canvas) { switch (mCurrentState) { case FOLDING_DOWN: case FOLDING_UP: drawYMotion(canvas); break; case FOLDING_RIGHT: case FOLDING_LEFT: drawXMotion(canvas); break; } canvas.drawPath(mPath, mAbovePaint); } private void drawXMotion(Canvas canvas) { canvas.drawArc(mOval, 90, 180, true, mFstHalfPaint); canvas.drawArc(mOval, -270, -180, true, mScndHalfPaint); mPath.reset(); mPath.moveTo(mHalf, 0); mPath.cubicTo(mAxisValue, 0, mAxisValue, mDiameter, mHalf, mDiameter); } private void drawYMotion(Canvas canvas) { canvas.drawArc(mOval, 0, -180, true, mFstHalfPaint); canvas.drawArc(mOval, -180, -180, true, mScndHalfPaint); mPath.reset(); mPath.moveTo(0, mHalf); mPath.cubicTo(0, mAxisValue, mDiameter, mAxisValue, mDiameter, mHalf); } @Override public void setColorFilter(ColorFilter cf) { this.mColorFilter = cf; mFstHalfPaint.setColorFilter(cf); mScndHalfPaint.setColorFilter(cf); mAbovePaint.setColorFilter(cf); } private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics()); } }