做个笔记,记录下怎么画圆弧
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();
}
}
}