Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clip a circular path inside a rectangle in Android

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.

like image 774
Can Poyrazoğlu Avatar asked Apr 05 '15 14:04

Can Poyrazoğlu


2 Answers

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

like image 162
Roj Avatar answered Oct 05 '22 01:10

Roj


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);
}
like image 20
Gil Moshayof Avatar answered Oct 05 '22 01:10

Gil Moshayof