Search code examples
javaandroidandroid-layoutandroid-viewandroid-custom-view

Use clipping to round corners of ViewGroup


I have a RelativeLayout that needs to have rounded upper left and upper right corners. I can do this with a drawable background defined in XML with corners topLeftRadius and topRightRadius. But... This RelativeLayout also need to have a background that is a layer-list with a tiled bitmap and shape combo, and the tiled bitmap does not have a corners parameter in the drawable XML. So my idea was to to make a RelativeLayout with the following code:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    path.reset();
    rect.set(0, 0, w, h);
    path.addRoundRect(rect, radius, radius, Path.Direction.CW);
    path.close();
}

@Override
protected void dispatchDraw(Canvas canvas) {
    int save = canvas.save();
    canvas.clipPath(path);
    super.dispatchDraw(canvas);
    canvas.restoreToCount(save);
}

Sadly no clipping is happening, I was expecting it to clip all four corners of my RelativeLayout, but nothing is happening. The "onSizeChanged" and "dispatchDraw" methods are both called, I tested that. I have also tried to turn off hardware acceleration, but it does nothing.

My RelativeLayout is part of a larger layout, and that layout is inflated in a subclass of FrameLayout, and that subclass is then used a a row in a RecyclerView, if that changes anything.


Solution

  • Having defined this layout:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent">
    
        <com.playground.RoundedRelativeLayout
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center"
            android:background="@color/colorPrimary" />
    
    </FrameLayout>
    

    Where RoundedRelativeLayout has following implementation:

    
        public class RoundedRelativeLayout extends RelativeLayout {
    
            private RectF rectF;
            private Path path = new Path();
            private float cornerRadius = 15;
    
            public RoundedRelativeLayout(Context context) {
                super(context);
            }
    
            public RoundedRelativeLayout(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
    
            public RoundedRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
                super(context, attrs, defStyleAttr);
            }
    
            @Override
            protected void onSizeChanged(int w, int h, int oldw, int oldh) {
                super.onSizeChanged(w, h, oldw, oldh);
                rectF = new RectF(0, 0, w, h);
                resetPath();
            }
    
            @Override
            public void draw(Canvas canvas) {
                int save = canvas.save();
                canvas.clipPath(path);
                super.draw(canvas);
                canvas.restoreToCount(save);
            }
    
            @Override
            protected void dispatchDraw(Canvas canvas) {
                int save = canvas.save();
                canvas.clipPath(path);
                super.dispatchDraw(canvas);
                canvas.restoreToCount(save);
            }
    
            private void resetPath() {
                path.reset();
                path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW);
                path.close();
            }
        }
    
    

    You'll get following output:

    The implementation is shamelessly stolen from RoundKornerLayouts project.