I am trying to make a view in android with rounded edges. The solution I found so far is to define a shape with rounded corners and use it as the background of that view.
Here is what I did, define a drawable as given below:
<padding
android:top="2dp"
android:bottom="2dp"/>
<corners android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>
Now I used this as the background for my layout as below:
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:clipChildren="true"
android:background="@drawable/rounded_corner">
This works perfectly fine, I can see that the view has rounded edges.
But my layout has got many other child views in it, say an ImageView or a MapView
. When I place an ImageView
inside the above layout, the corners of image are not clipped/cropped, instead it appears full.
I have seen other workarounds to make it work like the one explained here.
But is there a method to set rounded corners for a view and all its child views are contained within that main view that has rounded corners?
This question is related to
android
android-layout
view
android-view
android-shape
Jaap van Hengstum's answer works great however I think it is expensive and if we apply this method on a Button for example, the touch effect is lost since the view is rendered as a bitmap.
For me the best method and the simplest one consists in applying a mask on the view, like that:
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
float cornerRadius = <whatever_you_want>;
this.path = new Path();
this.path.addRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, Path.Direction.CW);
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (this.path != null) {
canvas.clipPath(this.path);
}
super.dispatchDraw(canvas);
}
Create a xml file called round.xml
in the drawable
folder and paste this content:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#FFFFFF" />
<stroke android:width=".05dp" android:color="#d2d2d2" />
<corners android:topLeftRadius="5dp" android:topRightRadius="5dp" android:bottomRightRadius="5dp" android:bottomLeftRadius="5dp"/>
</shape>
then use the round.xml
as background
to any item. Then it will give you rounded corners.
Difference from Jaap van Hengstum's answer:
public class RoundedFrameLayout extends FrameLayout {
private Bitmap mOffscreenBitmap;
private Canvas mOffscreenCanvas;
private BitmapShader mBitmapShader;
private Paint mPaint;
private RectF mRectF;
public RoundedFrameLayout(Context context) {
super(context);
init();
}
public RoundedFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setWillNotDraw(false);
}
@Override
public void draw(Canvas canvas) {
if (mOffscreenBitmap == null) {
mOffscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
mOffscreenCanvas = new Canvas(mOffscreenBitmap);
mBitmapShader = new BitmapShader(mOffscreenBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(mBitmapShader);
mRectF = new RectF(0f, 0f, canvas.getWidth(), canvas.getHeight());
}
super.draw(mOffscreenCanvas);
canvas.drawRoundRect(mRectF, 8, 8, mPaint);
}
}
shape.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#f6eef1" />
<stroke
android:width="2dp"
android:color="#000000" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
<corners android:radius="5dp" />
</shape>
and inside you layout
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:clipChildren="true"
android:background="@drawable/shape">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/your image"
android:background="@drawable/shape">
</LinearLayout>
If you are having problem while adding touch listeners to the layout. Use this layout as parent layout.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.FrameLayout;
public class RoundedCornerLayout extends FrameLayout {
private final static float CORNER_RADIUS = 6.0f;
private float cornerRadius;
public RoundedCornerLayout(Context context) {
super(context);
init(context, null, 0);
}
public RoundedCornerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void dispatchDraw(Canvas canvas) {
int count = canvas.save();
final Path path = new Path();
path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), cornerRadius, cornerRadius, Path.Direction.CW);
canvas.clipPath(path, Region.Op.REPLACE);
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.restoreToCount(count);
}
}
as
<?xml version="1.0" encoding="utf-8"?>
<com.example.view.RoundedCornerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/patentItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingRight="20dp">
... your child goes here
</RelativeLayout>
</com.example.view.RoundedCornerLayout>
In case you want to round some specific corner.
fun setCorners() {
val mOutlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
val left = 0
val top = 0;
val right = view.width
val bottom = view.height
val cornerRadiusDP = 16f
val cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDP, resources.displayMetrics).toInt()
// all corners
outline.setRoundRect(left, top, right, bottom, cornerRadius.toFloat())
/* top corners
outline.setRoundRect(left, top, right, bottom+cornerRadius, cornerRadius.toFloat())*/
/* bottom corners
outline.setRoundRect(left, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/
/* left corners
outline.setRoundRect(left, top, right + cornerRadius, bottom, cornerRadius.toFloat())*/
/* right corners
outline.setRoundRect(left - cornerRadius, top, right, bottom, cornerRadius.toFloat())*/
/* top left corner
outline.setRoundRect(left , top, right+ cornerRadius, bottom + cornerRadius, cornerRadius.toFloat())*/
/* top right corner
outline.setRoundRect(left - cornerRadius , top, right, bottom + cornerRadius, cornerRadius.toFloat())*/
/* bottom left corner
outline.setRoundRect(left, top - cornerRadius, right + cornerRadius, bottom, cornerRadius.toFloat())*/
/* bottom right corner
outline.setRoundRect(left - cornerRadius, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/
}
}
myView.apply {
outlineProvider = mOutlineProvider
clipToOutline = true
}
}
Or you can use a android.support.v7.widget.CardView
like so:
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@color/white"
card_view:cardCornerRadius="4dp">
<!--YOUR CONTENT-->
</android.support.v7.widget.CardView>
follow this tutorial and all the discussion beneath it - http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
according to this post written by Guy Romain, one of the leading developers of the entire Android UI toolkit, it is possible to make a container (and all his child views) with rounded corners, but he explained that it too expensive (from performances of rendering issues).
I'll recommend you to go according to his post, and if you want rounded corners, then implement rounded corners ImageView
according to this post. then, you could place it inside a container with any background, and you'll get the affect you wish.
that's what I did also also eventually.
The tutorial link you provided seems to suggest that you need to set the layout_width and layout_height properties, of your child elements to match_parent.
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent">
try this property with your linear layout it will help
tools:context=".youractivity"
Create a xml file under your drawable folder with following code. (The name of the file I created is rounded_corner.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- view background color -->
<solid
android:color="#a9c5ac" >
</solid>
<!-- view border color and width -->
<stroke
android:width="3dp"
android:color="#1c1b20" >
</stroke>
<!-- If you want to add some padding -->
<padding
android:left="4dp"
android:top="4dp"
android:right="4dp"
android:bottom="4dp" >
</padding>
<!-- Here is the corner radius -->
<corners
android:radius="10dp" >
</corners>
</shape>
And keep this drawable
as background
for the view to which you want to keep rounded corner border. Let’s keep it for a LinearLayout
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_corner"
android:layout_centerInParent="true">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hi, This layout has rounded corner borders ..."
android:gravity="center"
android:padding="5dp"/>
</LinearLayout>
The CardView
worked for me in API 27 in Android Studio 3.0.1. The colorPrimary
was referenced in the res/values/colors.xml
file and is just an example. For the layout_width of 0dp
it will stretch to the width of the parent. You'll have to configure the constraints and width/height to your needs.
<android.support.v7.widget.CardView
android:id="@+id/cardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/colorPrimary">
<!-- put your content here -->
</android.support.v7.widget.CardView>
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
Bitmap roundedBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap
.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(roundedBitmap);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return roundedBitmap;
}
Use shape in xml with rectangle.set the property of bottom or upper radius as want.then apply that xml as background to ur view....or...use gradients to do it from code.
public class RoundedCornerLayout extends FrameLayout {
private double mCornerRadius;
public RoundedCornerLayout(Context context) {
this(context, null, 0);
}
public RoundedCornerLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
public double getCornerRadius() {
return mCornerRadius;
}
public void setCornerRadius(double cornerRadius) {
mCornerRadius = cornerRadius;
}
@Override
public void draw(Canvas canvas) {
int count = canvas.save();
final Path path = new Path();
path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), (float) mCornerRadius, (float) mCornerRadius, Path.Direction.CW);
canvas.clipPath(path, Region.Op.REPLACE);
canvas.clipPath(path);
super.draw(canvas);
canvas.restoreToCount(count);
}
}
In Android L you will be able to just use View.setClipToOutline to get that effect. In previous versions there is no way to just clip the contents of a random ViewGroup in a certain shape.
You will have to think of something that would give you a similar effect:
If you only need rounded corners in the ImageView, you can use a shader to 'paint' the image over the shape you are using as background. Take a look at this library for an example.
If you really need every children to be clipped, maybe you can another view over your layout? One with a background of whatever color you are using, and a round 'hole' in the middle? You could actually create a custom ViewGroup that draws that shape over every children overriding the onDraw method.
With the Material Components Library the best way to make a View
with rounded corners is to use the MaterialShapeDrawable
.
Create a ShapeAppearanceModel with custom rounded corners:
ShapeAppearanceModel shapeAppearanceModelLL1 = new ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED,radius16)
.build();
Create a MaterialShapeDrawable
:
MaterialShapeDrawable shapeDrawableLL1 = new MaterialShapeDrawable(shapeAppearanceModeLL1);
If you want to apply also an elevationOverlay for the dark theme use this:
MaterialShapeDrawable shapeDrawableLL1 = MaterialShapeDrawable.createWithElevationOverlay(this, 4.0f);
shapeDrawableLL1.setShapeAppearanceModel(shapeAppearanceModelLL1);
Optional: apply to the shapeDrawable a background color and a stroke
shapeDrawableLL1.setFillColor(
ContextCompat.getColorStateList(this,R.color...));
shapeDrawableLL1.setStrokeWidth(2.0f);
shapeDrawableLL1.setStrokeColor(
ContextCompat.getColorStateList(this,R.color...));
Finally apply the shapeDrawable as background in your LinearLayout
(or other view):
LinearLayout linearLayout1= findViewById(R.id.ll_1);
ViewCompat.setBackground(linearLayout1,shapeDrawableLL1);
To create round corner image using com.google.android.material:material:1.2.0-beta01
float radius = context.getResources().getDimension(R.dimen.border_radius_hug);
shapeAppearanceModel = new ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED,radius)
.build();
imageView.setShapeAppearanceModel(shapeAppearanceModel)
or if you want to use it in xml file:
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/thumb"
android:layout_width="80dp"
android:layout_height="60dp"
app:shapeAppearanceOverlay="@style/circleImageView"
/>
in style.xml add this:
<style name="circleImageView" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">10%</item>
</style>
You can use an androidx.cardview.widget.CardView
like so:
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/dimen_4"
app:cardElevation="@dimen/dimen_4"
app:contentPadding="@dimen/dimen_10">
...
</androidx.cardview.widget.CardView>
OR
shape.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#f6eef1" />
<stroke
android:width="2dp"
android:color="#000000" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
<corners android:radius="5dp" />
</shape>
and inside you layout
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shape">
...
</LinearLayout>
Source: Stackoverflow.com