自定义圆弧圆角进度条

做个笔记,记录下怎么画圆弧

package com.myf.arcprogressbar;

import android.animation.TimeAnimator;
import android.animation.TimeAnimator.TimeListener;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * 颜色弧形进度条
 * 
 * @author Mengyf
 * 
 */
@SuppressLint({ "NewApi", "DefaultLocale" }) 
public class ColorArcProgressBar extends View {
	/** 背景画笔 */
	private Paint mPaintArcBg;
	/** 进度画笔 */
	private Paint mPaintArcProgress;
	/** 值画笔 */
	private Paint mCurSpeedPaint;
	/** 进度值 */
	private int mProgress;
	/** 最大值 */
	private int mMax = 100;
	/** 开始角度 */
	private float mStartAngle = 135;
	/** 扫描角度 */
	private float mSweepAngle = 270;
	/** 弧形线宽 */
	private float mStrokeWidth = 10;
	/** 扫描角度进度值 */
	protected float mSweepAngleProgress;
	/** 弧形线宽一般 */
	private float mStrokeWidthHalf = mStrokeWidth/2;
	/** 设置圆角还是非圆角 */
	private Cap mStrokeCap = Paint.Cap.ROUND;
	/** 画弧需要的矩形 */
	private RectF mOval = new RectF();
	/** 获取半径 */
	private int mRadius = 100;
	/** 渐变颜色 你可以添加很多种但是至少要保持在2种颜色以上 */
	private int[] mProgressColors = new int[] { Color.GREEN, Color.YELLOW, Color.RED, Color.RED };
	/** 背景颜色 */
	private int mBgColor = Color.BLACK;
	/** 进度颜色 */
	private int mProgressColor = Color.BLUE;
	/** 渐变色 */
	private Shader mSweepGradient;
	/** 颜色矩阵 */
	private Matrix mMatrix;
	/** 上一次渐变色 */
	private int[] mLastColors;
	/** 上一次半径 */
	private float mLastmRadius;
	/** 动画 */
	private TimeAnimator mTimeAnimator;
	/** 临时进度 */
	private float mTempSweepAngleProgress;
	/** 设置动画执行速度 */
	private float mAnimSpeed = 1f;
	/**  值文字大小 */
	private int mValueSize = 60 ;
	/**  单位文字大小 */
	private int mUnitSize = 20 ;
	/** 单位 */
	private String mUnit = "%";
	/**  值文字颜色 */
	private int mValueColor = Color.BLACK;
	/**  单位文字颜色 */
	private int mUnitColor = Color.BLACK;
	/** 显示文本内容 */
	private boolean mShowContent = true;
	/** 显示单位 */
	private boolean mShowUnit = true;
	/** 设置渐变 */
	private boolean isShade = true;
	
	private IOnProgressChangeListener mL;
	/** 记录上一次的值 */
	private int mLastProgress = -1;
    
    /**
     * 圆形进度
     * @param l
     */
    public void setOnProgressChangeListener(IOnProgressChangeListener l) {
    	mL = l;
	}
    
    /**
     * 进度值改变监听
     * @author ZH-SW-Mengyf
     *
     */
    public interface IOnProgressChangeListener{
    	/**
    	 * 环形进度条
    	 * @param rectangularView
    	 * @param progress
    	 */
    	void progressChange(ColorArcProgressBar rectangularView, int progress , boolean isAniming);
    }
    
	/**
	 * 通知进度改变
	 * @param progress
	 * @param isAniming
	 */
	private void notifyProgressChange(int progress , boolean isAniming) {
		if (mL != null) {
			if (mLastProgress != progress||!isAniming) {
				mLastProgress = progress;
				mL.progressChange(this, progress, isAniming);
			}
		}
	}
	
	public ColorArcProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init(context);
	}

	public ColorArcProgressBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public ColorArcProgressBar(Context context) {
		super(context);
		init(context);
	}

	private void init(Context context) {
		initBgPaint();
		initProgressPaint();
		initCurSpeedPain();
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		final int minimumWidth = getSuggestedMinimumWidth();
		final int minimumHeight = getSuggestedMinimumHeight();
		int width = measureWidth(minimumWidth, widthMeasureSpec);
		int height = measureHeight(minimumHeight, heightMeasureSpec);
		int minValue = Math.min(width, height);
		int radius = (int) ((minValue - mStrokeWidth) / 2);
		setRadius(radius);
		setMeasuredDimension(width, height);
	}
	
	/**
	 * 测量宽度
	 * @param defaultWidth
	 * @param measureSpec
	 * @return
	 */
	private int measureWidth(int defaultWidth, int measureSpec) {
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);
		switch (specMode) {
		case MeasureSpec.AT_MOST:
			defaultWidth = (int) (mRadius * 2 + mStrokeWidth);
			break;
			
		case MeasureSpec.EXACTLY:
			defaultWidth = specSize;
			break;
			
		case MeasureSpec.UNSPECIFIED:
			defaultWidth = Math.max(defaultWidth, specSize);
		}
		return defaultWidth;
	}
	
	/**
	 * 测量高度
	 * @param defaultHeight
	 * @param measureSpec
	 * @return
	 */
	private int measureHeight(int defaultHeight, int measureSpec) {
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);
		switch (specMode) {
		case MeasureSpec.AT_MOST:
			defaultHeight = (int) (mRadius * 2 + mStrokeWidth);
			break;
			
		case MeasureSpec.EXACTLY:
			defaultHeight = specSize;
			break;
			
		case MeasureSpec.UNSPECIFIED:
			defaultHeight = Math.max(defaultHeight, specSize);
			break;
		}
		return defaultHeight;

	}

	/**
	 * 初始化画笔
	 */
	private void initBgPaint() {
		mPaintArcBg = new Paint(); 
		mPaintArcBg.setAntiAlias(true);
		mPaintArcBg.setStyle(Style.STROKE);
		mPaintArcBg.setColor(mBgColor);
	}
	
	/**
	 * 初始化进度画笔
	 */
	private void initProgressPaint() {
		mPaintArcProgress = new Paint();
		mPaintArcProgress.setAntiAlias(true);
		mPaintArcProgress.setStyle(Style.STROKE);
		mPaintArcProgress.setColor(mProgressColor);
	}
	
	/**
	 * 绘制文字画笔
	 */
	private void initCurSpeedPain() {
        mCurSpeedPaint = new Paint();
        mCurSpeedPaint.setTextSize(mValueSize);
        mCurSpeedPaint.setColor(mValueColor);
        mCurSpeedPaint.setTextAlign(Paint.Align.CENTER);
	}
	
	
	@SuppressLint("DrawAllocation")
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		mOval.left = mStrokeWidthHalf + getPaddingStart();
		mOval.top = mStrokeWidthHalf + getPaddingTop();
		mOval.right = mRadius * 2 + mStrokeWidthHalf - getPaddingEnd();
		mOval.bottom = mRadius * 2 + mStrokeWidthHalf - getPaddingBottom();
		drawArcBg(canvas);
		drawArcProgress(canvas);
		drawText(canvas);
	}
	
	protected RectF getRectFOval() {
		return mOval;
	}
	
	/**
	 * 绘制背景
	 * @param canvas
	 */
	private void drawArcBg(Canvas canvas) {
		mPaintArcBg.setColor(mBgColor);
		mPaintArcBg.setStrokeWidth(mStrokeWidth);
		if (mStrokeCap != null) {
			mPaintArcBg.setStrokeCap(mStrokeCap);
		}
		
		mPaintArcBg.setColor(Color.BLACK);
		canvas.drawArc(mOval, mStartAngle, mSweepAngle, false, mPaintArcBg);
	}
	
	/**
	 * 绘制进度
	 * @param canvas
	 */
	private void drawArcProgress(Canvas canvas) {
		mPaintArcProgress.setStrokeWidth(mStrokeWidth);
		if (isShade) {
			if (mStrokeCap != null) {
				mPaintArcProgress.setStrokeCap(mStrokeCap);
			}
			
			mSweepGradient = getSweepGradient();
		
			if (mMatrix == null) {
				mMatrix = new Matrix();
			}
			
			//这两种都会有突出的部分,突出的部分为mStrokeWidth值的正方形
			if (Paint.Cap.ROUND == mStrokeCap || Paint.Cap.SQUARE == mStrokeCap) {
				mMatrix.setRotate(mStartAngle - mStrokeWidth, mRadius, mRadius);
			}else{
				mMatrix.setRotate(mStartAngle, mRadius, mRadius);
			}
			
			mSweepGradient.setLocalMatrix(mMatrix);
			mPaintArcProgress.setShader(mSweepGradient);
			
		}else{
			mPaintArcProgress.setColor(mProgressColor);
		}
		//因为是整型数据,所以大于等于1才开始绘制
		if (mProgress >= 1) {
			canvas.drawArc(mOval, mStartAngle, mSweepAngleProgress, false,mPaintArcProgress);
		}
	}

	/**
	 * 获取渐变色
	 * @return
	 */
	private Shader getSweepGradient() {
		if (mSweepGradient == null || mLastColors != mProgressColors || mLastmRadius != mRadius) {
			mLastColors = mProgressColors;
			mLastmRadius = mRadius;
			mSweepGradient = new SweepGradient(mRadius, mRadius, mProgressColors, null);
		}
		return mSweepGradient;
	}
	
	/**
	 * 绘制文本内容
	 * @param canvas
	 */
	private void drawText(Canvas canvas) {
		if (mShowContent) {
			String value = String.valueOf(mProgress);
			String unit = mUnit;
			mCurSpeedPaint.setTextSize(mValueSize);
			mCurSpeedPaint.setColor(mValueColor);
			Paint.FontMetrics fontMetrics = mCurSpeedPaint.getFontMetrics();
			// 为基线到字体上边框的距离,即上图中的top
			float top = fontMetrics.top;
			// 为基线到字体下边框的距离,即上图中的bottom
			float bottom = fontMetrics.bottom;
			// 基线中间点的y轴计算公式
			int baseLineY = (int) (mRadius - top / 2 - bottom / 2);
			
			float valueW = mCurSpeedPaint.measureText(value);
			float valueX = mRadius + getPaddingStart()/2 - getPaddingEnd()/2;
			float valueY = baseLineY + getPaddingTop()/2 - getPaddingBottom()/2;
			canvas.drawText(value, valueX, valueY, mCurSpeedPaint);
			
			if (mShowUnit) {
				mCurSpeedPaint.setTextSize(mUnitSize);
				mCurSpeedPaint.setColor(mUnitColor);
				float unitW = mCurSpeedPaint.measureText(unit);
				float unitX = (mRadius + valueW / 2 + unitW / 2) + getPaddingStart()/2 - getPaddingEnd()/2;
				float unitY = baseLineY + getPaddingTop()/2 - getPaddingBottom()/2;
				canvas.drawText(unit, unitX , unitY, mCurSpeedPaint);
			}
		}
	}
	
	/**
	 * 设置渐变
	 */
	public void setShade(boolean isShade) {
		this.isShade = isShade;
	}
	
	/**
	 * 设置文字大小
	 * @param valueSize 值文字大小
	 * @param unitSize 单位文字大小
	 */
	public void setTextSize(int valueSize ,int unitSize) {
		mValueSize = valueSize;
		mUnitSize = unitSize;
		invalidate();
	}

	/**
	 * 文本状态
	 * @param showContent 显示内容
	 * @param showUnit 显示单位
	 */
	public void setTextState(boolean showContent, boolean showUnit) {
		mShowContent = showContent;
		mShowUnit = showUnit;
		invalidate();
	}
	
	/**
	 * 设置单位
	 * @param unit
	 */
	public void setTextUnit(String unit) {
		mUnit = unit;
		invalidate();
	}
	
	/**
	 * 设置文字大小
	 * @param valueColor 值文字大小
	 * @param unitColor 单位文字大小
	 */
	public void setTextColor(int valueColor ,int unitColor) {
		mValueColor = valueColor;
		mUnitColor = unitColor;
		invalidate();
	}
	
	/**
	 * 设置颜色数组
	 * @param colors
	 */
	public void setProgressColors(int[] colors) {
		this.mProgressColors = colors;
		invalidate();
	}
	
	/**
	 * 设置背景颜色
	 * @param color
	 */
	public void setBgColor(int color) {
		this.mBgColor = color;
		invalidate();
	}
	
	/**
	 * 设置进度颜色值
	 * @param color
	 */
	public void setProgressColor(int color) {
		this.mProgressColor = color;
		invalidate();
	}

	/**
	 * 设置是否为圆角
	 * @param cap
	 */
	public void setStrokeCap(Cap cap) {
		mStrokeCap = cap;
		invalidate();
	}
	
	/**
	 * 设置半径
	 * @param radius
	 */
	public void setRadius(int radius) {
		mRadius = radius;
		requestLayout();
	}
	
	/**
	 * 获取半径
	 * @return
	 */
	public int getRadius() {
		return mRadius;
	}

	/**
	 * 设置圆弧线宽
	 * @param strokeWidth
	 */
	public void setStrokeWidth(float strokeWidth) {
		this.mStrokeWidth = strokeWidth;
		mStrokeWidthHalf = strokeWidth / 2;
		invalidate();
	}
	
	/**
	 * 获取线宽
	 */
	public float getStrokeWidth() {
		return mStrokeWidth;
	}

	/**
	 * 开始角度
	 * 
	 * @param startAngle
	 */
	public void setStartAngle(float startAngle) {
		mStartAngle = startAngle;
		invalidate();
	}
	
	/**
	 * 获取开始角度
	 * @return
	 */
	public float getStartAngle() {
		return mStartAngle;
	}

	/**
	 * 扫描角度
	 * 
	 * @param sweepAngle
	 */
	public void setSweepAngle(float sweepAngle) {
		mSweepAngle = sweepAngle;
		invalidate();
	}
	
	/**
	 * 获取扫描角度
	 * @return
	 */
	public float getSweepAngle() {
		return mSweepAngle;
	}

	/**
	 * 设置进度值
	 * @param progress
	 */
	public void setProgress(int progress) {
		setProgress(progress,false);
	}
	
	/**
	 * 设置进度
	 * 
	 * @param progress
	 * @param fromUser
	 */
	public void setProgress(int progress, boolean anim) {
		if (progress < 0) {
			progress = 0;
		}

		if (progress > mMax) {
			progress = mMax;
		}

		if (progress != mProgress) {
			mProgress = progress;
			refreshProgress(mProgress,anim);
		}
	}

	/**
	 * 获取进度
	 * 
	 * @return
	 */
	public int getProgress() {
		return mProgress;
	}

	/**
	 * 设置最大值
	 * 
	 * @param max
	 */
	public void setMax(int max) {
		if (max < 0) {
			max = 0;
		}
		if (max != mMax) {
			mMax = max;
			postInvalidate();

			if (mProgress > max) {
				mProgress = max;
			}
			refreshProgress(mProgress, false);
		}
	}

	/**
	 * 或者最大值
	 * 
	 * @return
	 */
	public int getMax() {
		return mMax;
	}

	/**
	 * 刷新进度值
	 * 
	 * @param progress
	 */
	protected void refreshProgress(int progress , boolean isAnim) {
		if (isAnim) {
			setAnim(progress * getK());
		}else{
			mProgress = progress;
			refreshAngle(progress * getK());
			notifyProgressChange(mProgress, false);
		}
	}

	/**
	 * 设置动画执行的速度
	 * 
	 * @param animSpeed
	 */
	public void setAnimSpeed(float animSpeed) {
		if (animSpeed < 0.1) {
			animSpeed = 0.1f;
		}
		mAnimSpeed = animSpeed;
	}
	
	/**
	 * 刷新进度值
	 * 
	 * @param progress
	 */
	protected void refreshAngle(float angle) {
		stopAnim();
		mSweepAngleProgress = getValidAngle(angle);
		invalidate();
	}
	
	/**
	 * 获取一个进度值所代表的角度
	 * @return
	 */
	private float getK() {
		return mSweepAngle / mMax;
	}
	
	@SuppressLint("NewApi") 
	private void setAnim(final float angle) {
		mTempSweepAngleProgress = angle;
		Log.d("myf", "mTempSweepAngleProgress:"+mTempSweepAngleProgress);
		
		if (mTimeAnimator == null) {
			mTimeAnimator = new TimeAnimator();
			mTimeAnimator.setTimeListener(new TimeListener() {
				
				@Override
				public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
					Log.d("ttt", "onTimeUpdate totalTime:"+totalTime+"====deltaTime:"+deltaTime);
					if (mTempSweepAngleProgress > mSweepAngleProgress) {
						mSweepAngleProgress = getTempSweepAngleProgress(true);
						mProgress = (int) (mSweepAngleProgress / getK());
						notifyProgressChange(mProgress, true);
						if (mTempSweepAngleProgress <= mSweepAngleProgress) {
							mSweepAngleProgress = mTempSweepAngleProgress;
							notifyProgressChange(mProgress, false);
							stopAnim();
						}
					} 
					else if (mTempSweepAngleProgress < mSweepAngleProgress) {
						mSweepAngleProgress = getTempSweepAngleProgress(false);
						mProgress = (int) (mSweepAngleProgress / getK());
						notifyProgressChange(mProgress, true);
						if (mTempSweepAngleProgress >= mSweepAngleProgress) {
							mSweepAngleProgress = mTempSweepAngleProgress;
							notifyProgressChange(mProgress, false);
							stopAnim();
						}
					} 
					else {
						stopAnim();
					}
					
					postInvalidate();
				}
			});
		}
		
		startAnim();
	}
	
	/**
	 * 获取有效的角度
	 * @return
	 */
	protected float getValidAngle(float angle) {
		if (angle > mSweepAngle) {
			angle = mSweepAngle;
		}
		if (angle < 0) {
			angle = 0;
		}
		return angle;

	}
	
	/**
	 * 
	 * @param isAdd
	 * @return
	 */
	private float getTempSweepAngleProgress(boolean isAdd) {
		float sweepAngleProgress;
		if (isAdd) {
			sweepAngleProgress = mSweepAngleProgress + mAnimSpeed;
		} else {
			sweepAngleProgress = mSweepAngleProgress - mAnimSpeed;
		}
		sweepAngleProgress = getValidAngle(sweepAngleProgress);
		return sweepAngleProgress;

	}
	
	/**
	 * 开机动画
	 */
	private void startAnim() {
		if (mTimeAnimator != null && !mTimeAnimator.isStarted()) {
			mTimeAnimator.start();
		}
	}

	/**
	 * 结束动画
	 */
	private void stopAnim() {
		if (mTimeAnimator != null && mTimeAnimator.isStarted()) {
			mTimeAnimator.end();
		}
	}
	
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值