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.
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.
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.
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.
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()
}
}
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"/>
.
.
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With