Search code examples
androidcanvasviewandroid-custom-viewondraw

How to increment arch length in custom view with OnClick?


I have a custom view(Circle) which is partly filled with arch(red color). Here is the picture https://gyazo.com/72e19cb97fd9f2adac2259c3855cf136.

I want to divide my custom view into sections, and when the button is clicked I draw an arch. 1 click 1/5 is covered with arch, 2nd click 2/5, etc...till 5.

How do I fill my view with red Arch when i press Increment button?(I don't understand the drawing part)

Here is what I have already tried - My CustomView class:

public class MySimpleView extends View {

private static final int PAINT_FLAGS = Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG;
private static final int STROKE_WIDTH = 40;
private static final int SECTIONS = 5;
private Paint basePaint, degreesPaint, centerPaint, rectPaint;
private RectF rect;
private int centerX, centerY, radius;
private int fullArchSliceLength;
private int colorArchLineLength;

public MySimpleView(Context context) {
    super(context);
    init();
}

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

public MySimpleView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    rectPaint = new Paint(PAINT_FLAGS);
    rectPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
    rectPaint.setStyle(Paint.Style.FILL);

    centerPaint = new Paint(PAINT_FLAGS);
    centerPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
    centerPaint.setStyle(Paint.Style.FILL);

    basePaint = new Paint(PAINT_FLAGS);
    basePaint.setStyle(Paint.Style.STROKE);
    basePaint.setStrokeWidth(STROKE_WIDTH);
    basePaint.setColor(ContextCompat.getColor(getContext(), R.color.darkGrey));

    degreesPaint = new Paint(PAINT_FLAGS);
    degreesPaint.setStyle(Paint.Style.STROKE);
    degreesPaint.setStrokeCap(Paint.Cap.ROUND);
    degreesPaint.setStrokeJoin(Paint.Join.ROUND);

    degreesPaint.setStrokeWidth(STROKE_WIDTH);
    degreesPaint.setColor(Color.RED);

    fullArchSliceLength = 360 / SECTIONS;
    colorArchLineLength = fullArchSliceLength - 2;

}

public void swapColor() {
    degreesPaint.setColor(degreesPaint.getColor() == Color.RED ? Color.GREEN :
            Color.RED);
    postInvalidate();
}


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

    if (rect == null) {
        centerX = getMeasuredWidth() / 2;
        centerY = getMeasuredHeight() / 2;
        radius = Math.min(centerX, centerY);

        int startTop = STROKE_WIDTH / 2;
        int startLeft = STROKE_WIDTH / 2;

        int endBottom = 2 * radius - startTop;
        int endRight = 2 * radius - startTop;

        rect = new RectF(startTop, startLeft, endRight, endBottom);
    }

    canvas.drawRect(rect, rectPaint);

    canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, basePaint);
    // TODO: 2019-04-26 LOOK HERE

    for (int i = 3; i < SECTIONS; i++) {
        canvas.drawArc(rect, i * fullArchSliceLength,colorArchLineLength,
                false, degreesPaint);
    }
    // TODO: 2019-04-26 LOOK HERE
    //        canvas.drawArc(rect, 0F, 90F, false, degreesPaint);

   canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, centerPaint);
}
}

Solution

  • public class MySimpleView extends View {
    
        private static final int PAINT_FLAGS = Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG;
        private static final int STROKE_WIDTH = 40;
        private static final int SECTIONS = 5;
        private Paint basePaint, degreesPaint, centerPaint, rectPaint;
        private RectF rect;
        private int centerX, centerY, radius;
        private int fullArchSliceLength;
        private int colorArchLineLength;
        private int currentSections = 1;
    
        public MySimpleView(Context context) {
            super(context);
            init();
        }
    
        public MySimpleView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public MySimpleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            rectPaint = new Paint(PAINT_FLAGS);
            rectPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
            rectPaint.setStyle(Paint.Style.FILL);
    
            centerPaint = new Paint(PAINT_FLAGS);
            centerPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
            centerPaint.setStyle(Paint.Style.FILL);
    
            basePaint = new Paint(PAINT_FLAGS);
            basePaint.setStyle(Paint.Style.STROKE);
            basePaint.setStrokeWidth(STROKE_WIDTH);
            basePaint.setColor(ContextCompat.getColor(getContext(), android.R.color.darker_gray));
    
            degreesPaint = new Paint(PAINT_FLAGS);
            degreesPaint.setStyle(Paint.Style.STROKE);
            degreesPaint.setStrokeCap(Paint.Cap.ROUND);
            degreesPaint.setStrokeJoin(Paint.Join.ROUND);
    
            degreesPaint.setStrokeWidth(STROKE_WIDTH);
            degreesPaint.setColor(Color.RED);
    
            fullArchSliceLength = 360 / SECTIONS;
            colorArchLineLength = fullArchSliceLength - 2;
    
        }
    
        //just a simple increment function
        public void increment() {
            if (currentSections < SECTIONS) {
                currentSections++;
                postInvalidate();
            }
        }
    
        public void swapColor() {
            degreesPaint.setColor(degreesPaint.getColor() == Color.RED ? Color.GREEN :
                    Color.RED);
            postInvalidate();
        }
    
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            if (rect == null) {
                centerX = getMeasuredWidth() / 2;
                centerY = getMeasuredHeight() / 2;
                radius = Math.min(centerX, centerY);
    
                int startTop = STROKE_WIDTH / 2;
                int startLeft = STROKE_WIDTH / 2;
    
                int endBottom = 2 * radius - startTop;
                int endRight = 2 * radius - startTop;
    
                rect = new RectF(startTop, startLeft, endRight, endBottom);
            }
    
            canvas.drawRect(rect, rectPaint);
    
            canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, basePaint);
    
            /*
            startAngle is set to 270 so it will start at the top.
            0 is right
            90 bottom
            180 left
            270 top
             */
            canvas.drawArc(rect, 270, currentSections * colorArchLineLength, false, degreesPaint);
    
            canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, centerPaint);
        }
    }
    

    Basically your logic was a bit wrong. When calling drawArc the first parameter will be the startAngle of your line (meaning does the line start on top, left, right, bottom of the circle). I have written in comments what each degree corresponds to. The sweepAngle is how many degrees you are drawing (which you had already calculated correctly). Hope it works as you would expect it!