Android:带间隔的自定义圆形进度条

3
我正在尝试创建一个自定义视图,其中包含一个圆圈,在其中,我必须在运行时拥有如下图所示的部分。我在onDraw方法中尝试了很多东西,但没有成功。我甚至尝试了https://github.com/donvigo/CustomProgressControls。基本上,我想给出一些部分,然后在每个部分中,我可以根据需要选择颜色。

enter image description here

我正在寻找一个进度条ProgressBar,它应该像图片中显示的那样有间隔/空格,在圆圈之间。比如说,如果我已经给了5个部分,其中3个应该是“满的”,它应该将前3个涂成红色,其余2个涂成绿色。

我现在的绘制方式是:

private void initExternalCirclePainter() {
    internalCirclePaint = new Paint(); 
    internalCirclePaint.setAntiAlias(true);    
    internalCirclePaint.setStrokeWidth(internalStrokeWidth);  
    internalCirclePaint.setColor(color); 
    internalCirclePaint.setStyle(Paint.Style.STROKE);
    internalCirclePaint.setPathEffect(new DashPathEffect(new float[]{dashWith, dashSpace}, dashSpace));
} 

这意味着您需要一个带有5个部分的进度条,但每个部分的颜色不同,我理解得对吗? - Vishal Patel
1
请查看Canvas#drawArc - pskink
Canvas#drawArc 我已经尝试了,但是我没有得到两个部分之间的完美间距。 - nick
所以你需要检查一下你的数学。 - pskink
编辑了问题并删除了一些注释。 - nick
显示剩余5条评论
2个回答

2

我可能有些晚了,但是我实际上编写了一个自定义组件,其中有两个环,看起来与您试图实现的相似。您可以轻松地删除外部环。最终我得到的图像:

enter image description here

这是类:

public class RoundedSectionProgressBar extends View {
// The amount of degrees that we wanna reserve for the divider between 2 sections
private static final float DIVIDER_ANGLE = 7;
public static final float DEGREES_IN_CIRCLE = 360;
public static final int PADDING = 18;
public static final int PADDING2 = 12;

protected final Paint paint = new Paint();
protected final Paint waitingPaint = new Paint();
protected final Paint backgroundPaint = new Paint();
private int totalSections = 5;
private int fullSections = 2;
private int waiting = 3; // The outer ring. You can omit this
private RectF rect = new RectF();

public RoundedSectionProgressBar(Context context) {
    super(context);
    init(context, null);
}

public RoundedSectionProgressBar(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
}

public RoundedSectionProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
    // Can come from attrs if need be?
    int strokeWidth = 3;
    setupPaint(context, strokeWidth, paint, R.color.filled_color_inner_ring);
    setupPaint(context, strokeWidth, waitingPaint, R.color.empty_color_inner_ring);
    setupPaint(context, strokeWidth, backgroundPaint, R.color.filled_color_outer_ring);
}

private void setupPaint(Context context, int strokeWidth, Paint backgroundPaint, int colorRes) {
    backgroundPaint.setStrokeCap(Paint.Cap.SQUARE);
    backgroundPaint.setColor(context.getResources().getColor(colorRes));
    backgroundPaint.setAntiAlias(true);
    backgroundPaint.setStrokeWidth(strokeWidth);
    backgroundPaint.setStyle(Paint.Style.STROKE);
}

public int getTotalSections() {
    return totalSections;
}

public void setTotalSections(int totalSections) {
    this.totalSections = totalSections;
    invalidate();
}

public int getFullSections() {
    return fullSections;
}

public void setNumberOfSections(int fullSections, int totalSections, int waiting) {
    this.fullSections = fullSections;
    this.totalSections = totalSections;
    this.waiting = waiting;
    invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    rect.set(getLeft() + PADDING, getTop() + PADDING, getRight() - PADDING, getBottom() - PADDING);
    float angleOfSection = (DEGREES_IN_CIRCLE / totalSections) - DIVIDER_ANGLE;
    // Drawing the inner ring
    for (int i = 0; i < totalSections; i++) {
        // -90 because it doesn't start at the top, so rotate by -90
        // divider_angle/2 especially in 2 sections, it's visibly rotated by Divider angle, so we split this between last and first
        float startAngle = -90 + i * (angleOfSection + DIVIDER_ANGLE) + DIVIDER_ANGLE / 2;
        if (i < fullSections) {
            canvas.drawArc(rect, startAngle, angleOfSection, false, paint);
        } else {
            canvas.drawArc(rect, startAngle, angleOfSection, false, backgroundPaint);
        }
    }
    // Drawing the outer ring
    rect.set(getLeft() + PADDING2, getTop() + PADDING2, getRight() - PADDING2, getBottom() - PADDING2);
    for (int i = 0; i < waiting; i++) {
        float startAngle = -90 + i * (angleOfSection + DIVIDER_ANGLE) + DIVIDER_ANGLE / 2;
        canvas.drawArc(rect, startAngle, angleOfSection, false, waitingPaint);
    }
}
}

请注意,该代码不会给出外圈的“空”插槽,因为最终我们决定不使用它们。内圆将同时拥有空和填充插槽。整个类可以重复使用,只负责绘制的两个环,6/6+3和红色圆圈是另一个视图的一部分。
代码中最重要的部分是onDraw方法。它包含循环中绘制弧形的逻辑,以及计算角度并在它们之间添加空格的逻辑。一切都被旋转了-90度,因为我需要它从顶部开始,而不是像Android中的0度角在右侧。它并不复杂,如果需要,您可以修改它以更好地适应您的需求。

0

我发现对于绘制弧(基于部分数的角度值进行操作),做数学计算要比计算弧长更容易。 这是一个快速的想法,有很多硬编码属性,但你应该能够理解:

public class MyStrokeCircleView extends View {
    private Paint mPaint;
    private RectF mRect;
    private int mPadding;

    private int mSections;
    private int mFullArcSliceLength;
    private int mColorArcLineLength;
    private int mArcSectionGap;

    public MyStrokeCircleView(Context context) {
        super(context);
        init(null, 0);
    }

    public MyStrokeCircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public MyStrokeCircleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        mPaint = new Paint();
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.darker_gray));

        mPadding = 5;
        mRect = new RectF(mPadding, mPadding, mPadding, mPadding);

        mSections = 4;
        mFullArcSliceLength = 360 / mSections;
        mArcSectionGap = mFullArcSliceLength / 10;
        mColorArcLineLength = mFullArcSliceLength - 2 * mArcSectionGap;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mRect.right = getWidth() - mPadding;
        mRect.bottom = getHeight() - mPadding;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (int i = 0; i < mSections; i++) {
            canvas.drawArc(mRect, i * mFullArcSliceLength + mArcSectionGap, mColorArcLineLength, false, mPaint);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接