Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 313
Neigaard Avatar asked Nov 10 '17 13:11

Neigaard


People also ask

How do I round corners in XML?

To do that right click on drawable (under resources), choose New, choose Drawable Resource File. Name the resource rounded_blue_border and make the root element shape. We will use the attributes stroke, corners and padding. The stroke determines the width and color of the border.

How do I make rounded corners in paint?

In the upper left corner, select “Draw Filled Shape“. Draw the rounded rectangle over the area you would like to keep for your rounded corners image. Use the Magic Wand to select the area of the rounded rectangle.


2 Answers

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.

like image 172
azizbekian Avatar answered Oct 17 '22 23:10

azizbekian


Here is a Kotlin version of azizbekian's answer:

class RoundedRelativeLayout(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {

    private lateinit var rectF: RectF
    private val path = Path()
    private var cornerRadius = 15f

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        rectF = RectF(0f, 0f, w.toFloat(), h.toFloat())
        resetPath()
    }

    override fun draw(canvas: Canvas) {
        val save = canvas.save()
        canvas.clipPath(path)
        super.draw(canvas)
        canvas.restoreToCount(save)
    }

    override fun dispatchDraw(canvas: Canvas) {
        val save = canvas.save()
        canvas.clipPath(path)
        super.dispatchDraw(canvas)
        canvas.restoreToCount(save)
    }

    private fun resetPath() {
        path.reset()
        path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW)
        path.close()
    }
}

Edit

As a bonus here's how to add the cornerRadius as an extra xml attribute you can set, just add this to res/values/styleable.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundedRelativeLayout">
        <attr name="cornerRadius" format="float"/>
    </declare-styleable>
</resources>

and then add this init method to the RoundedRelativeLayout class:

init {
    val ta = getContext().obtainStyledAttributes(attrs, R.styleable.RoundedRelativeLayout)
    cornerRadius = ta.getFloat(R.styleable.RoundedRelativeLayout_cornerRadius, 15f)
    ta.recycle()
}

And now when you use the layout you can set the cornerRadius in the xml like this:

<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto" <-- Make sure you include this line
        android:layout_width="80dp"
        android:layout_height="80dp">

    .
    .
    .

    <your.package.name.RoundedRelativeLayout
            android:id="@+id/roundedRect"
            app:cornerRadius="24"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    .
    .
    .

like image 20
Quinn Avatar answered Oct 17 '22 23:10

Quinn