In Android, an ImageView is a rectangle by default. How can I make it a rounded rectangle (clip off all 4 corners of my Bitmap to be rounded rectangles) in the ImageView?
Clipping to rounded shapes was added to the View class in API 21.
Just do this:
res/drawable/round_outline.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp" />
...
</shape>
android:background="@drawable/round_outline"
android:clipToOutline="true"
Unfortunately, there's a bug and that XML attribute is not recognized. Luckily, we can still set up clipping in Java:
ImageView.setClipToOutline(true)
Here's what it will look like:
Note:
This method works for any drawable shape (not just rounded). It will clip the ImageView to whatever shape outline you've defined in your Drawable xml.
Special note about ImageViews
setClipToOutline()
only works when the View's background is set to a shape drawable. If this background shape exists, View treats the shape's outline as the borders for clipping and shadowing purposes.
This means, if you want to use setClipToOutline()
to round the corners on an ImageView, your image must be set using android:src
instead of android:background
(since background must be set to your rounded shape). If you MUST use background to set your image instead of src, you can use this workaround:
There is a cool library that allows you to shape imageviews.
Here is an example:
<com.github.siyamed.shapeimageview.mask.PorterShapeImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:siShape="@drawable/shape_rounded_rectangle"
android:src="@drawable/neo"
app:siSquare="true"/>
Shape definition:
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:topLeftRadius="18dp"
android:topRightRadius="18dp"
android:bottomLeftRadius="18dp"
android:bottomRightRadius="18dp" />
<solid android:color="@color/black" />
</shape>
Result:
Starting with the version 1.2.0-alpha03
of the the Material Components Library there is the new ShapeableImageView
.
You can use something like:
<com.google.android.material.imageview.ShapeableImageView
...
app:shapeAppearanceOverlay="@style/roundedImageView"
app:srcCompat="@drawable/ic_image" />
with:
<style name="roundedImageView" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">8dp</item>
</style>
Or programmatically:
float radius = getResources().getDimension(R.dimen.default_corner_radius);
imageView.setShapeAppearanceModel(imageView.getShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED,radius)
.build());
My implementation of ImageView with rounded corners widget, that (down||up)sizes image to required dimensions. It utilizes code form CaspNZ.
public class ImageViewRounded extends ImageView {
public ImageViewRounded(Context context) {
super(context);
}
public ImageViewRounded(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ImageViewRounded(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onDraw(Canvas canvas) {
BitmapDrawable drawable = (BitmapDrawable) getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap fullSizeBitmap = drawable.getBitmap();
int scaledWidth = getMeasuredWidth();
int scaledHeight = getMeasuredHeight();
Bitmap mScaledBitmap;
if (scaledWidth == fullSizeBitmap.getWidth() && scaledHeight == fullSizeBitmap.getHeight()) {
mScaledBitmap = fullSizeBitmap;
} else {
mScaledBitmap = Bitmap.createScaledBitmap(fullSizeBitmap, scaledWidth, scaledHeight, true /* filter */);
}
Bitmap roundBitmap = ImageUtilities.getRoundedCornerBitmap(getContext(), mScaledBitmap, 5, scaledWidth, scaledHeight,
false, false, false, false);
canvas.drawBitmap(roundBitmap, 0, 0, null);
}
}
As of recently, there is another way - using Glide's Generated API. It takes some initial work but then gives you all the power of Glide with the flexibility to do anything because you writhe the actual code so I think it's a good solution for the long run. Plus, the usage is very simple and neat.
First, setup Glide version 4+:
implementation 'com.github.bumptech.glide:glide:4.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
Then create Glid's app module class to trigger the annotation processing:
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}
Then create the Glide extension which actually does the work. You can customize it to do whatever you want:
@GlideExtension
public class MyGlideExtension {
private MyGlideExtension() {}
@NonNull
@GlideOption
public static RequestOptions roundedCorners(RequestOptions options, @NonNull Context context, int cornerRadius) {
int px = Math.round(cornerRadius * (context.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
return options.transforms(new RoundedCorners(px));
}
}
After adding these files, build your project.
Then use it in your code like this:
GlideApp.with(this)
.load(imageUrl)
.roundedCorners(getApplicationContext(), 5)
.into(imageView);
While the above answer works, Romain Guy (a core Android developer) shows a better method in his blog which uses less memory by using a shader not creating a copy of the bitmap. The general gist of the functionality is here:
BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rect = new RectF(0.0f, 0.0f, width, height);
// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);
The advantages of this over other methods is that it:
I've created a RoundedImageView based off this code that wraps this logic into an ImageView and adds proper ScaleType
support and an optional rounded border.