自定义view实例:圆形进度条绘制

    xiaoxiao2022-07-06  209

    前言:由于项目要求,需要绘制一个圆形的进度条以进行学生成绩答题选项中,各个选项正确率的表示。同时需要根据是否达到要求显示不同的颜色。

    代码实现:

    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来实现,但这里基本功能是可以实现的。具体的效果图这里暂时就不贴出来了。本人水平有限,如存在问题,欢迎大神指出。

    最新回复(0)