How to clip a circular path inside a rectangle in Android
Asked Answered
T

2

13

I've read over 20 questions/answers but I still can't get what I want. I want to cut a circle inside a rectangle as seen below:

enter image description here

Here is my code:

@Override
protected void onDraw(Canvas canvas) {
    Paint paint = new Paint();
    paint.setStyle(Paint.Style.FILL);
    paint.setARGB(180, 0, 0, 0);
    canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
    Path circularPath = new Path();
    circularPath.addCircle(getWidth() / 2, getHeight() / 2, radius, Path.Direction.CCW);
    canvas.clipPath(circularPath, Region.Op.REPLACE);
    canvas.drawColor(0x00000000);


}

My background (setARGB) displays correctly, however nothing is clipped. I've also tried different Op values other than REPLACE, forced software rasterization (as I've read on some Android versions clipPath doesn't support some of the Ops) by calling setLayerType(LAYER_TYPE_SOFTWARE, null); on constructor, but no avail. How do I achieve the desired effect?

Note: My minimum SDK version is 15, so I don't need to support anything lower than 4.0.

Tripod answered 5/4, 2015 at 14:17 Comment(6)
did you try Region.Op.DIFFERENCE ?Shier
@Shier yes, just tried now again to make sure. Unfortunatelly, nothing happens.Nutmeg
DIFFERENCE just works fine to me, try to canvas.drawColor with Color.RED, then clipPath and then drawColor 0x88000000Shier
@Shier could you post a sscce as an answer?Nutmeg
just use like this: canvas.drawColor(Color.RED); Path circularPath = new Path(); circularPath.addCircle(getWidth() / 2, getHeight() / 2, 300, Path.Direction.CCW); canvas.clipPath(circularPath, Region.Op.DIFFERENCE); canvas.drawColor(0x66000000);Shier
@Shier it works. please post this comment as an answer so I can accept it.Nutmeg
S
6

Use clipPath before drawRect.

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

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

    mPaint.setAntiAlias(true);
    mPaint.setColor(Color.WHITE);
    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawPaint(mPaint);

    float rectWidth = Utils.dpToPx(100.0f);

    Path circularPath = new Path();
    circularPath.addCircle(width / 2.0f, rectWidth / 2.0f, rectWidth / 3.0f, Path.Direction.CCW);
    canvas.clipPath(circularPath, Region.Op.DIFFERENCE);

    mPaint.setColor(Color.BLUE);
    canvas.drawRect((width - rectWidth) / 2.0f, 0.0f, ((width - rectWidth) / 2.0f) + rectWidth, rectWidth, mPaint);
}

Result

Semen answered 15/10, 2018 at 19:31 Comment(0)
S
4

Try clipping your path in dispatchDraw():

@Override
protected void dispatchDraw(Canvas canvas)
{
    canvas.clipPath(mClipPath, mRegion); // previously created path & region

    super.dispatchDraw(canvas);
}

Remove the path clipping code from your onDraw method, and that should do it.

Edit:

When creating your path, make sure you do so only after a measure occurred, for example:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    mClipPath.reset();
    float radius = Math.min((float)getMeasuredWidth() / 2f, (float)getMeasuredHeight() / 2f) + 5;
    mClipPath.addCircle((float)getMeasuredWidth() / 2f, (float)getMeasuredHeight() / 2f, radius, Path.Direction.CCW);
}
Siva answered 5/4, 2015 at 14:26 Comment(3)
Tried it, nothing changed.Nutmeg
I've edited my answer to include a possible cause to why it's not working for youSiva
Copy/pasted it. Still no change. I heavily doubt there should be an overridden method other than onDraw though, as all the examples I've seen involve only that method.Nutmeg

© 2022 - 2025 — McMap. All rights reserved.