package com.tools.customviewdemo; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.PathEffect; import android.graphics.Typeface; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.text.TextPaint; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; /** * * @author wj * @date 2018/1/20 * api用法:{@link{setStepNum()设置目标步数和当前步数,刻度线的个数必须为360的因数}} */ public class CircleScaleView extends View { private Context mContext; private float mViewWidth; private float mViewHeight; private float mCircleRadius; private int mScaleLineLength; private int mLineNum = 36; private int mGoalStep = 16000; private int mCurrentStep = 14628; private int mRedLineNum; private int mLastRedLineNum; private String mGoalText; private String mSteps; private Paint mPaint; private TextPaint mTextPaint; private int bottomColorWhite; private int upColorRed; private int goalColor; private PathEffect mEffect; private float mDashWidth; public CircleScaleView(Context context) { this(context, null); } public CircleScaleView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CircleScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mContext = context; mGoalText = context.getResources().getString(R.string.goal_text); mSteps = context.getResources().getString(R.string.steps); bottomColorWhite = ContextCompat.getColor(context, R.color.bottomColorWhite); upColorRed = ContextCompat.getColor(context, R.color.upColorRed); goalColor = ContextCompat.getColor(context, R.color.goalColor); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(DipUtils.dip2px(context, 2)); mDashWidth = DipUtils.dip2px(context, 2); mEffect = new DashPathEffect(new float[]{mDashWidth, mDashWidth * 4, mDashWidth, mDashWidth * 4}, 1); mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextAlign(Paint.Align.CENTER); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mViewWidth = getWidth(); mViewHeight = getHeight(); mCircleRadius = (float) (mViewWidth * 0.35); mScaleLineLength = (int) (mViewWidth * 0.06); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // draw bottom circle canvas.translate(mViewWidth / 2, mViewHeight / 2); mPaint.setColor(bottomColorWhite); mPaint.setPathEffect(mEffect); mPaint.setStrokeCap(Paint.Cap.SQUARE); mPaint.setStrokeWidth(DipUtils.dip2px(mContext, 2.5f)); canvas.drawCircle(0, 0, mCircleRadius, mPaint); // draw text mTextPaint.setTextSize(mCircleRadius / 6); mTextPaint.setTypeface(Typeface.MONOSPACE); mTextPaint.setColor(goalColor); canvas.drawText(mGoalText + mGoalStep, 0, -mCircleRadius / 2, mTextPaint); mTextPaint.setTextSize((float) (mCircleRadius / 2.3)); mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL)); mTextPaint.setColor(bottomColorWhite); canvas.drawText(mCurrentStep + "", 0, DipUtils.dip2px(mContext, 10), mTextPaint); mTextPaint.setTextSize(mCircleRadius / 5); canvas.drawText(mSteps, DipUtils.dip2px(mContext, 10), mCircleRadius / 2, mTextPaint); // draw bitmap canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.icon_step), DipUtils.dip2px(mContext, 5) -mCircleRadius / 2, mCircleRadius / 2 - DipUtils.dip2px(mContext, 20), mPaint); // draw scale line mPaint.setPathEffect(null); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(DipUtils.dip2px(mContext, 5)); mPaint.setColor(bottomColorWhite); for (int i = 0; i < mLineNum; i++) { canvas.drawLine(0, -(mCircleRadius + mScaleLineLength + DipUtils.dip2px(mContext, 10)), 0, -(mCircleRadius + DipUtils.dip2px(mContext, 10)), mPaint); canvas.rotate(360 / mLineNum); } // draw red line mPaint.setColor(upColorRed); for (int i = 0; i < mRedLineNum; i++) { canvas.drawLine(0, -(mCircleRadius + mScaleLineLength + DipUtils.dip2px(mContext, 10)), 0, -(mCircleRadius + DipUtils.dip2px(mContext, 10)), mPaint); canvas.rotate(360 / mLineNum); } } public void setStepNum(int goalStep, int currentStep) { mGoalStep = goalStep; mCurrentStep = currentStep; final int redLineNum = mLineNum * currentStep / goalStep; ValueAnimator animator = ValueAnimator.ofInt(mLastRedLineNum, redLineNum); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { if ((int)(animation.getAnimatedValue()) != mRedLineNum || (int)(animation.getAnimatedValue()) == redLineNum) { mRedLineNum = (int) animation.getAnimatedValue(); invalidate(); } } }); animator.setDuration(Math.abs((redLineNum - mLastRedLineNum) * 100)); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start(); mLastRedLineNum = redLineNum; } }