前言:由于项目要求,需要绘制一个圆形的进度条以进行学生成绩答题选项中,各个选项正确率的表示。同时需要根据是否达到要求显示不同的颜色。
代码实现:
public class AnalysisProgressBar extends View {
private Paint progressPaint;
/**
* 绘制背景圆弧的画笔
*/
private Paint bgPaint;
private float progressNum;
/**
* 进度条开始角度
*/
private float startAngle;
private float sweepAngle;
private RectF mRectF;
/**
* 控件宽度
*/
private float barWidth;
/**
* 进度条宽度
*/
private float progressWith;
/**
* progressBar的编号
*/
private String progressBarLabel;
private int correctProgressColor;
private int errorProgressColor;
private int bgColor;
private float labelTextSize;
/**
* 进度的最大数值
*/
private static float MAX_PROGRESS_NUM = 100f;
/**
* 圆的角度
*/
private static int CIRCLE_ANGLE = 360;
/**
* 背景图旋转间隔角度
*/
private static int ROTATE_DEGREES = 10;
/**
* 百分比数值距离进度条的高度
*/
private float paddingTopOfProgress;
/**
* 百分比
*/
private String percentageText;
private float percentageTextSize;
private int percentageTextColor;
private Paint percentPaint;
/**
* 区分是正确进度还是错误进度样式
*/
private boolean isCorrectTypeBar;
private Handler mHandler;
private BaseActivity mCurrentActivity;
public AnalysisProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AnalysisProgressBarView);
correctProgressColor = typedArray.getColor(R.styleable.AnalysisProgressBarView_correctProgressColor, Color.parseColor("#57D7A5"));
errorProgressColor = typedArray.getColor(R.styleable.AnalysisProgressBarView_errorProgressColor, Color.parseColor("#CCCCCC"));
bgColor = typedArray.getColor(R.styleable.AnalysisProgressBarView_progressBgColor, Color.parseColor("#EBEBEB"));
barWidth = typedArray.getDimension(R.styleable.AnalysisProgressBarView_barWidth, UIUtil.dip2px(context, 60));
progressWith = typedArray.getDimension(R.styleable.AnalysisProgressBarView_progressWidth, UIUtil.dip2px(context, 5));
paddingTopOfProgress = typedArray.getDimension(R.styleable.AnalysisProgressBarView_progressWidth, UIUtil.dip2px(context, 12));
labelTextSize = typedArray.getDimension(R.styleable.AnalysisProgressBarView_labelTextSize, UIUtil.dip2px(context, 17));
percentageTextSize = typedArray.getDimension(R.styleable.AnalysisProgressBarView_percentageTextSize, UIUtil.dip2px(context, 14));
percentageTextColor = typedArray.getColor(R.styleable.AnalysisProgressBarView_progressBgColor, Color.parseColor("#333333"));
mHandler = new Handler();
typedArray.recycle();
initPaint();
}
private void initPaint() {
progressPaint = new Paint();
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setColor(errorProgressColor);
progressPaint.setAntiAlias(true);
progressPaint.setStrokeWidth(progressWith);
bgPaint = new Paint();
bgPaint.setStyle(Paint.Style.FILL);
bgPaint.setColor(bgColor);
bgPaint.setAntiAlias(true);
bgPaint.setStrokeWidth(2);
progressNum = 0f;
startAngle = -90;
sweepAngle = 0f;
percentPaint = new Paint();
percentPaint.setTextSize(percentageTextSize);
percentPaint.setColor(percentageTextColor);
percentPaint.setAntiAlias(true);
Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/montserrat_regular.otf");
percentPaint.setTypeface(typeface);
mRectF = new RectF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int requestHeight = (int) (barWidth + paddingTopOfProgress + percentageTextSize + progressWith);
int height = measureSize(requestHeight, heightMeasureSpec);
int width = measureSize((int) barWidth, widthMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureSize(int defaultSize, int measureSpec) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
if (specMode == View.MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(barWidth / 2, barWidth / 2);
//绘制中间编号
if (progressBarLabel != null) {
progressPaint.setStrokeWidth(2);
progressPaint.setAntiAlias(true);
progressPaint.setColor(getProgressColorByType(isCorrectTypeBar));
progressPaint.setStyle(Paint.Style.FILL);
progressPaint.setTextSize(labelTextSize);
progressPaint.setTypeface(Typeface.DEFAULT_BOLD);
float measureText = progressPaint.measureText(progressBarLabel);
canvas.drawText(progressBarLabel, -measureText / 2, labelTextSize / 2 - 8, progressPaint);
}
//绘制背景图
canvas.translate(-barWidth / 2, 0);
//绘制梯形
Path pathBg = new Path();
pathBg.lineTo(0, 3);
pathBg.lineTo(0, -3);
pathBg.lineTo(progressWith, -2);
pathBg.lineTo(progressWith, 2);
pathBg.lineTo(0, 2);
canvas.save();
for (int i = 0; i <= CIRCLE_ANGLE; i += ROTATE_DEGREES) {
canvas.drawPath(pathBg, bgPaint);
canvas.rotate(ROTATE_DEGREES, barWidth / 2, 0);
}
canvas.restore();
//绘制进度
if (mRectF.isEmpty()) {
mRectF.left = progressWith / 2;
mRectF.top = progressWith / 2;
mRectF.right = (int) barWidth - progressWith / 2;
mRectF.bottom = (int) barWidth - progressWith / 2;
}
//坐标回到原点
canvas.translate(0, -barWidth / 2);
sweepAngle = (CIRCLE_ANGLE / MAX_PROGRESS_NUM) * mCurrentProgress - 5;
progressPaint.setStyle(Paint.Style.STROKE);
//设置画笔画出的形状
progressPaint.setStrokeCap(Paint.Cap.ROUND);
progressPaint.setStrokeJoin(Paint.Join.ROUND);
progressPaint.setStrokeWidth(progressWith);
canvas.drawArc(mRectF, startAngle, sweepAngle, false, progressPaint);
//绘制底部百分比
canvas.translate(barWidth / 2, barWidth + paddingTopOfProgress + percentageTextSize);
percentPaint.setColor(getPercentageTextColorByType(isCorrectTypeBar));
percentPaint.setTypeface(Typeface.DEFAULT_BOLD);
float drawTextIndex = percentPaint.measureText(percentageText);
if (percentageText != null) {
canvas.drawText(percentageText, -drawTextIndex / 2, 0, percentPaint);
}
}
private void setCurrentProgress(int indexProgress) {
this.mCurrentProgress = indexProgress;
this.percentageText = indexProgress + "%";
invalidate();
}
int mCurrentProgress = 0;
Runnable mProgressAnimationRunnable = new Runnable() {
@Override
public void run() {
if (mCurrentActivity != null && mCurrentActivity.isDestroyed()) {
clear();
return;
}
mCurrentProgress++;
setCurrentProgress(mCurrentProgress);
mHandler.postDelayed(this, 5);
if (mCurrentProgress == progressNum || mCurrentProgress == MAX_PROGRESS_NUM) {
mHandler.removeCallbacks(this);
}
}
};
/**
* 在页面销毁时调用该方法清除进度动画的计时,防止内存溢出
*/
private void clear() {
if (mHandler != null && mProgressAnimationRunnable != null) {
mHandler.removeCallbacks(mProgressAnimationRunnable);
mHandler = null;
mProgressAnimationRunnable = null;
}
}
/**
* 通过bar的类型获取对应的进度条颜色
*
* @param isCorrectTypeBar bar类型
* @return 进度条颜色
*/
private int getProgressColorByType(boolean isCorrectTypeBar) {
return isCorrectTypeBar ? correctProgressColor : errorProgressColor;
}
/**
* 通过bar的类型获取对应百分比字体颜色
*
* @param isCorrectTypeBar bar类型
* @return 百分比字体颜色
*/
private int getPercentageTextColorByType(boolean isCorrectTypeBar) {
return isCorrectTypeBar ? correctProgressColor : percentageTextColor;
}
//******************************以下为对外提供的方法***********************************************
/**
* 设置进度条的类型
*
* @param correctTypeBar 是否为正确类型的bar
*/
public void setCorrectTypeBar(boolean correctTypeBar) {
isCorrectTypeBar = correctTypeBar;
}
/**
* 设置进度
*
* @param progressNum 进度的值
* @param currentActivity 当前activity
*/
public void setProgressNum(int progressNum, BaseActivity currentActivity) {
this.mCurrentActivity = currentActivity;
this.progressNum = progressNum;
this.percentageText = progressNum + "%";
// invalidate();
mHandler.postDelayed(mProgressAnimationRunnable, 0);
}
/**
* 设置进度条的编号
*
* @param progressBarLabel 编号
*/
public void setProgressBarLabel(String progressBarLabel) {
this.progressBarLabel = progressBarLabel;
invalidate();
}
}
此外,关于自定义属性,需要在values文件夹下创建attr.xml文件进行定义即可,如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AnalysisProgressBarView">
<attr name="correctProgressColor" format="color"></attr>
<attr name="errorProgressColor" format="color"></attr>
<attr name="progressBgColor" format="color"></attr>
<attr name="barWidth" format="dimension"></attr>
<attr name="progressWidth" format="dimension"></attr>
<attr name="paddingTopOfProgress" format="dimension"></attr>
<attr name="percentageTextSize" format="dimension"></attr>
<attr name="labelTextSize" format="dimension"></attr>
</declare-styleable>
</resources>
最后,在xml文件中直接使用即可。
具体实现过程及代码逻辑在注释中已经解释的比较清楚了,就不再一一展开说明了。代码中有些地方还需要进一步优化,比如进度的更新完全可以使用valueAnimation来实现,但这里基本功能是可以实现的。具体的效果图这里暂时就不贴出来了。本人水平有限,如存在问题,欢迎大神指出。