自定义圆形进度条

背景

开发过程当中,当我们进行上传、下载或者更新等项目需求时,往往需要给用户展示一个进度值用于显示当前的更新状态,便于用户进行操作处理。当原生的控件满足不了展示需要时,就要我们自己定制所需要的展示效果,值此情景时各种各样的自定义进度展示控件便应用而生。以下简单介绍几种常见的显示样式,实现的方式大同小异,大家可以根据自己的需求简单修改配置即可。

效果图:
进度条

常见的使用样式

ProgressWheel使用

1.在attr.xml中定义属性

<declare-styleable name="ProgressWheel">
    <!-- 文本内容 -->
    <attr name="pwText" format="string" />
    <!-- 文本字体颜色 -->
    <attr name="pwTextColor" format="color" />
    <!-- 文本字体大小 -->
    <attr name="pwTextSize" format="dimension" />
    <!-- 进度条颜色 -->
    <attr name="pwBarColor" format="color" />
    <!-- 进度条宽度 -->
    <attr name="pwBarWidth" format="integer" />
    <!-- 进度条默认长度 -->
    <attr name="pwDefaultProgress" format="integer" />
    <!-- 默认轮廓颜色 -->
    <attr name="pwRimColor" format="color" />
    <!-- 默认轮廓宽度 -->
    <attr name="pwRimWidth" format="integer" />
    <!-- 圆圈内部的颜色 -->
    <attr name="pwCircleInnerColor" format="color" />
    <!-- 控制外边缘颜色 -->
    <attr name="pwOuterEdgeColor" format="color" />
    <!-- 控制外边缘大小 -->
    <attr name="pwOuterEdgeSize" format="dimension" />
    <!-- 控制内边缘颜色 -->
    <attr name="pwInnerEdgeColor" format="color" />
    <!-- 控制内边缘大小 -->
    <attr name="pwInnerEdgeSize" format="dimension" />
</declare-styleable>

2.布局文件

<com.wiggins.progresswheel.widget.ProgressWheel
    android:id="@+id/mProgressWheel"
    android:layout_width="150dp"
    android:layout_height="150dp"
    android:layout_marginTop="@dimen/margin_normal"
    app:pwBarColor="@color/red"
    app:pwBarWidth="15"
    app:pwDefaultProgress="49"
    app:pwRimColor="@color/orange"
    app:pwRimWidth="15" />

3.java文件调用

mProgressWheel.setProgress(pg);

4.绘制元素边界

private void setupBounds() {
    // 为了保持宽度和长度的一致,我们要获得layout_width和layout_height中较小的一个,从而绘制一个圆
    int minValue = Math.min(layoutWidth, layoutHeight);

    // 计算在绘制过程中在x,y方向的偏移量
    int xOffset = layoutWidth - minValue;
    int yOffset = layoutHeight - minValue;

    // 间距加上偏移量
    paddingTop = this.getPaddingTop() + (yOffset / 2);
    paddingBottom = this.getPaddingBottom() + (yOffset / 2);
    paddingLeft = this.getPaddingLeft() + (xOffset / 2);
    paddingRight = this.getPaddingRight() + (xOffset / 2);

    int width = getWidth();
    int height = getHeight();

    innerEdgeBounds = new RectF(
            paddingLeft + (1.5f * barWidth),
            paddingTop + (1.5f * barWidth),
            width - paddingRight - (1.5f * barWidth),
            height - paddingBottom - (1.5f * barWidth));

    outerEdgeBounds = new RectF(
            paddingLeft + barWidth,
            paddingTop + barWidth,
            width - paddingRight - barWidth,
            height - paddingBottom - barWidth);

    innerEdgeContour = new RectF(
            outerEdgeBounds.left + (rimWidth / 2.0f) + (outerEdgeSize / 2.0f),
            outerEdgeBounds.top + (rimWidth / 2.0f) + (outerEdgeSize / 2.0f),
            outerEdgeBounds.right - (rimWidth / 2.0f) - (outerEdgeSize / 2.0f),
            outerEdgeBounds.bottom - (rimWidth / 2.0f) - (outerEdgeSize / 2.0f));

    outerEdgeContour = new RectF(
            outerEdgeBounds.left - (rimWidth / 2.0f) - (innerEdgeSize / 2.0f),
            outerEdgeBounds.top - (rimWidth / 2.0f) - (innerEdgeSize / 2.0f),
            outerEdgeBounds.right + (rimWidth / 2.0f) + (innerEdgeSize / 2.0f),
            outerEdgeBounds.bottom + (rimWidth / 2.0f) + (innerEdgeSize / 2.0f));
}

5.绘制元素属性

private void setupPaints() {
   //进度条
    barPaint.setColor(barColor);//设置画笔颜色
    barPaint.setAntiAlias(true);//设置抗锯齿
    barPaint.setStyle(Style.STROKE);//设置画笔为空心
    barPaint.setStrokeWidth(barWidth);//设置线宽

    //圆环
    rimPaint.setColor(rimColor);
    rimPaint.setAntiAlias(true);
    rimPaint.setStyle(Style.STROKE);
    rimPaint.setStrokeWidth(rimWidth);

    //环内
    circleInnerPaint.setColor(circleInnerColor);
    circleInnerPaint.setAntiAlias(true);
    circleInnerPaint.setStyle(Style.FILL);

    //字体
    textPaint.setColor(textColor);
    textPaint.setStyle(Style.FILL);
    textPaint.setAntiAlias(true);
    textPaint.setTextSize(textSize);

    //外边缘
    outerEdgePaint.setColor(outerEdgeColor);
    outerEdgePaint.setAntiAlias(true);
    outerEdgePaint.setStyle(Style.STROKE);
    outerEdgePaint.setStrokeWidth(outerEdgeSize);

    //内边缘
    innerEdgePaint.setColor(innerEdgeColor);
    innerEdgePaint.setAntiAlias(true);
    innerEdgePaint.setStyle(Style.STROKE);
    innerEdgePaint.setStrokeWidth(innerEdgeSize);
}

6.绘制视图

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //绘制边界
    canvas.drawArc(innerEdgeBounds, 360, 360, false, circleInnerPaint);
    canvas.drawArc(outerEdgeBounds, 360, 360, false, rimPaint);

    //绘制边缘
    canvas.drawArc(outerEdgeContour, 360, 360, false, outerEdgePaint);
    canvas.drawArc(innerEdgeContour, 360, 360, false, innerEdgePaint);

    //绘制进度
    canvas.drawArc(outerEdgeBounds, -90, progress, false, barPaint);

    //绘制文字(并让它显示在圆水平和垂直方向的中心处)
    float textHeight = textPaint.descent() - textPaint.ascent();
    float verticalTextOffset = (textHeight / 2) - textPaint.descent();

    for (String line : splitText) {
        float horizontalTextOffset = textPaint.measureText(line) / 2;
        canvas.drawText(
                line,
                this.getWidth() / 2 - horizontalTextOffset,
                this.getHeight() / 2 + verticalTextOffset,
                textPaint);
    }
}
RoundProgressBar使用

1.在attr.xml中定义属性

<declare-styleable name="RoundProgressBar">
    <!-- 圆环颜色 -->
    <attr name="roundColor" format="color" />
    <!-- 圆环宽度 -->
    <attr name="roundWidth" format="float" />
    <!-- 进度条颜色 -->
    <attr name="progressColor" format="color" />
    <!-- 默认进度百分比 -->
    <attr name="progressRatio" format="float" />
    <!-- 字体颜色 -->
    <attr name="textColors" format="color" />
    <!-- 字体大小 -->
    <attr name="textSizes" format="dimension" />
    <!-- 是否显示进度百分比 -->
    <attr name="textIsShow" format="boolean" />
</declare-styleable>

2.布局文件

<com.wiggins.progresswheel.widget.RoundProgressBar
    android:id="@+id/mRoundProgressBar"
    android:layout_width="150dp"
    android:layout_height="150dp"
    app:progressColor="@color/blue"
    app:roundColor="@color/red" />

3.java文件调用

mRoundProgressBar.setProgress(new float[]{getProgress(56), getProgress(90),
                getProgress(120), getProgress(88), getProgress(80)},
        new String[]{"#A0DD2A", "#FFAF8B", "#36D9F1", "#FFD71C", "#A89AFF"});

4.绘制元素边界

private void setupBounds() {
    int minValue = Math.min(getWidth(), getHeight());
    int centre = minValue / 2; // 获取圆心的x坐标
    int radius = (int) (centre - mRoundWidth / 2); // 圆环的半径
    mBounds = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);
}

5.绘制元素属性

private void setupPaints() {
    mRoundPaint.setColor(mRoundColor);// 设置画笔颜色
    mRoundPaint.setAntiAlias(true); // 消除锯齿
    mRoundPaint.setStyle(Style.STROKE); // 设置空心
    mRoundPaint.setStrokeWidth(mRoundWidth);// 设置线宽

    mTextPaint.setColor(mTextColor);
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
    mTextPaint.setStyle(Paint.Style.FILL);
    mTextPaint.setAntiAlias(true);

    mProgressPaint.setStrokeWidth(mRoundWidth);
    mProgressPaint.setAntiAlias(true);
    mProgressPaint.setStyle(Style.STROKE);
}

6.绘制视图

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 绘制边界
    canvas.drawArc(mBounds, 0, 360, false, mRoundPaint);

    // 绘制进度百分比
    int percent = Math.round((mProgressRatio / 360) * 100);
    // 测量字体宽度
    float textWidth = mTextPaint.measureText(percent + "%");
    // 绘制的起点X轴坐标:画布宽度的一半 - 文字宽度的一半
    int baseX = (int) (canvas.getWidth() / 2 - textWidth / 2);
    // 绘制的起点Y轴坐标:画布高度的一半 - 文字总高度的一半
    int baseY = (int) ((canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));

    // 绘制进度
    float start = -90;
    if (mRatio != null) {
        for (int i = 0; i < mRatio.length; i++) {
            mProgressPaint.setColor(Color.parseColor(mColors[i]));// 进度的颜色
            canvas.drawArc(mBounds, start, mRatio[i], false, mProgressPaint);
            start += mRatio[i];
        }
    } else {
        mProgressPaint.setColor(mProgressColor); // 进度的颜色
        canvas.drawArc(mBounds, start, mProgressRatio, false, mProgressPaint);
        if (mTextIsShow) {
            canvas.drawText(percent + "%", baseX, baseY, mTextPaint);
        }
    }
}
RingProgressBar使用

1.在attr.xml中定义属性

<declare-styleable name="RingProgressBar">
    <!-- 圆环颜色 -->
    <attr name="ringColor" format="color" />
    <!-- 圆环宽度 -->
    <attr name="ringWidth" format="dimension" />
    <!-- 圆环是否空心 -->
    <attr name="ringIsStroke" format="boolean" />
    <!-- 进度颜色 -->
    <attr name="ringProgressColor" format="color" />
    <!-- 字体颜色 -->
    <attr name="textColor" format="color" />
    <!-- 字体大小 -->
    <attr name="textSize" format="dimension" />
    <!-- 是否显示中间的进度值 -->
    <attr name="textIsDisplayable" format="boolean" />
    <!-- 最大进度值 -->
    <attr name="maxProgress" format="integer" />
    <!-- 进度条的风格:实心或者空心 -->
    <attr name="ringProgressStyle">
        <enum name="STROKE" value="0" />
        <enum name="FILL" value="1" />
    </attr>
</declare-styleable>

2.布局文件

<com.wiggins.progresswheel.widget.RingProgressBar
    android:id="@+id/mRingProgressBar"
    android:layout_width="150dp"
    android:layout_height="150dp"
    android:layout_marginLeft="@dimen/margin_normal"
    app:maxProgress="100"
    app:ringColor="@color/blue"
    app:ringIsStroke="false"
    app:ringProgressColor="@color/orange"
    app:ringWidth="7dp" />

3.java文件调用

mRingProgressBar.setCurrentProgress(63);

4.绘制视图

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    /**
     * 绘制默认圆环
     */
    int centre = getWidth() / 2; // 获取圆心的x坐标
    int radius = (int) (centre - mRingWidth / 2); // 圆环的半径
    mRingPaint.setColor(mRingColor); // 圆环颜色
    mRingPaint.setStrokeWidth(mRingWidth); // 圆环宽度
    mRingPaint.setAntiAlias(true); // 消除锯齿
    if (mRingIsStroke) {
        mRingPaint.setStyle(Paint.Style.STROKE); // 设置空心
    } else {
        mRingPaint.setStyle(Paint.Style.FILL); // 设置实心
    }
    canvas.drawCircle(centre, centre, radius, mRingPaint); // 画出圆环

    /**
     * 绘制进度百分比
     */
    mTextPaint.setColor(mTextColor);
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
    mTextPaint.setStyle(Paint.Style.FILL);
    mTextPaint.setAntiAlias(true);
    // 进度百分比
    int percent = (int) (((float) mCurrentProgress / (float) mMaxProgress) * 100);
    // 测量字体宽度
    float textWidth = mTextPaint.measureText(percent + "%");

    // 绘制的起点X轴坐标:画布宽度的一半 - 文字宽度的一半
    int baseX = (int) (canvas.getWidth() / 2 - textWidth / 2);
    // 绘制的起点Y轴坐标:画布高度的一半 - 文字总高度的一半
    int baseY = (int) ((canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));

    if (mTextIsDisplayable && mRingProgressStyle == STROKE) {
        canvas.drawText(percent + "%", baseX, baseY, mTextPaint);
    }

    /**
     * 绘制进度的圆弧
     */
    mProgressPaint.setStrokeWidth(mRingWidth);
    mProgressPaint.setColor(mRingProgressColor);
    mProgressPaint.setAntiAlias(true);
    RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius); // 用于定义的圆弧的形状和大小的界限
    switch (mRingProgressStyle) {
        case STROKE: {
            mProgressPaint.setStyle(Paint.Style.STROKE);
            canvas.drawArc(oval, 270, 360 * mCurrentProgress / mMaxProgress, false, mProgressPaint); // 根据进度画圆弧
            break;
        }
        case FILL: {
            mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            if (mCurrentProgress != 0) {
                canvas.drawArc(oval, 270, 360 * mCurrentProgress / mMaxProgress, true, mProgressPaint); // 根据进度画圆弧
            }
            break;
        }
    }
}

项目地址 ☞ 传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值