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

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

代码实现:

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易小四

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值