ChatGPT解决这个技术问题 Extra ChatGPT

How to make an ImageView with rounded corners?

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?

Note that from 2021 onwards, simply use ShapeableImageView

Hidden below older, more complicated answers is what I think should be the accepted answer now: RoundedBitmapDrawable, added in v4 Support Library revision 21.
You may do it easiest way just using the CardView with an ImageView inside - look the example here stackoverflow.com/a/41479670/4516797
This library is very useful.
Material Design 1.2.0 introduced ShapeableImageView So might be it's useful.

S
SilentCloud

This is pretty late in response, but for anyone else that is looking for this, you can do the following code to manually round the corners of your images.

http://www.ruibm.com/?p=184

This isn't my code, but I've used it and it's works wonderfully. I used it as a helper within an ImageHelper class and extended it just a bit to pass in the amount of feathering I need for a given image.

Final code looks like this:

package com.company.app.utils;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;

public class ImageHelper {
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
                .getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        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 output;
    }
}

It doesnot work for all the devices.Do I need change anywhere ?
It takes nearly 0.03 second to do that for a 200*200 picture, so I do think that's not best solution.
It only rounds top left and top right corners. Why ?
@vinc3m1 solution here github.com/makeramen/RoundedImageView works really well! also see his answer (stackoverflow.com/a/15032283/2048266)
Not working well when trying to set imageview's scaletype, only fitXY is working, centerCrop and other are showing unpredictable results, anyone here got same issues?
T
Taras Vovkovych

Another easy way is to use a CardView with the corner radius and an ImageView inside:

  <androidx.cardview.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:cardCornerRadius="8dp"
            android:layout_margin="5dp"
            android:elevation="10dp">

            <ImageView
                android:id="@+id/roundedImageView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/image"
                android:background="@color/white"
                android:scaleType="centerCrop"
                />
        </androidx.cardview.widget.CardView>

https://i.stack.imgur.com/g1hbB.jpg


Seems like a good workaround. But it doesn't clip images on pre-Lollipops.
What if , we only want radius on the top left and top right corners and not all corners ?
Very simple and smart way to apply curve border in any view.
Good solution but elevation is only supported in api level 21 and above
To disable card elevation use app:cardElevation="0dp" (not android:elevation)
h
hungryghost

Clipping to rounded shapes was added to the View class in API 21.

Just do this:

Create a rounded shape drawable, something like this:

res/drawable/round_outline.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    ...
</shape>

Set the drawable as your ImageView's background: android:background="@drawable/round_outline"

According to this documentation, then all you need to do is add android:clipToOutline="true"

Unfortunately, there's a bug and that XML attribute is not recognized. Luckily, we can still set up clipping in Java:

In your activity or fragment: ImageView.setClipToOutline(true)

Here's what it will look like:

https://i.stack.imgur.com/6NlvO.png

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:

Create a layout and set its background to your shape drawable

Wrap that layout around your ImageView (with no padding)

The ImageView (including anything else in the layout) will now display with rounded layout shape.


Error:(x) No resource identifier found for attribute 'clipToOutline' in package 'android'
Instead android:clipToOutline, one must use android:outlineProvider="background".
Another reason I "love" Google so much is that this bug is almost my age and still resides in its small world. Google seems to be giving no cr*ap about it ))
I seem to have no success with this method when I only set topleft and topRight corner in the background shape to rounded.
@AnuMartin this is known issue - issuetracker.google.com/issues/37036728
D
David d C e Freitas

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:

does not create a separate copy of the bitmap, which uses a lot of memory with large images [vs most of the other answers here]

supports antialisasing [vs clipPath method]

supports alpha [vs xfermode+porterduff method]

supports hardware acceleration [vs clipPath method]

only draws once to the canvas [vs xfermode and clippath methods]

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.


in your / example / res / layout / rounded_item.xml why do you specify an image src when all your sources are hardcoded ? Nice demo, just way overkill.
your sample has a serious out-of-memory issue, just like the original sample of Romain Guy. I still don't know what causes it, but just like his code, this is a really hard thing to find. If you can't see a crash from the app because of OOM, you can rotate the app multiple times till it occurs (depends on your device, ROM, etc...) . I've reported about it in the past here: stackoverflow.com/questions/14109187/…
Nobody else is reporting the out of memory issue, so it must be something you're doing outside of the code that is incorrect. The example correctly holds a limited set of bitmaps in the adapter without recreating them every time a view is drawn. The example shown here is a snippet of the draw() method in the Drawable, which uses the reference to the original bitmap it holds and works correctly. It is not meant to change the original bitmap, but only render it with rounded corners. Center crop works fine in the example as well.
If you can provide screenshots or devices where it doesn't work, or even go as far as offering a fix and pull request, that would be most productive. I've tested rotating multiple times on my devices and memory stays the same.
Note that it won't work if the image size is above 2048 pixels. The shader doesn't support a texture to be larger than that.
G
Gabriele Mariotti

Starting with the version 1.2.0-alpha03 of 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 in your themes.xml:

<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());

https://i.stack.imgur.com/iEnDb.png

With jetpack compose you can apply a clip Modifier using a RoundedCornerShape:

Image(
    painter = painterResource(R.drawable.xxxx),
    contentDescription = "xxxx",
    contentScale = ContentScale.Crop,            
    modifier = Modifier
        .size(64.dp)
        .clip(RoundedCornerShape(8.dp))             
)

If image has background color, this currently produces the background color to pop under the rounded corners. This also ignores scaleType property on image.
It is not working when we increase size programmatically
Simple solution, works with root layout background
J
Jonik

In the v21 of the Support library there is now a solution to this: it's called RoundedBitmapDrawable.

It's basically just like a normal Drawable except you give it a corner radius for the clipping with:

setCornerRadius(float cornerRadius)

So, starting with Bitmap src and a target ImageView, it would look something like this:

RoundedBitmapDrawable dr = RoundedBitmapDrawableFactory.create(res, src);
dr.setCornerRadius(cornerRadius);
imageView.setImageDrawable(dr);

Great thanks, here's the next step: stackoverflow.com/questions/24878740/…
actually there is android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory so v4 also supports it.
@deadfish yes its in v4 support library but not until REVISION 21 of the support library
This is solution is simple and up-to-date. It requires the least amount of code and have great extensibility. Can work with images from local file, cached drawable, or even with Volley's NetworkImageView. I highly agree that this should be the accepted answer nowadays, as @Jonik pointed out.
Unfortutately doesn't work with scaleType centerCrop (support library v25.3.1)
S
Shlok Jhawar

A quick xml solution -

<android.support.v7.widget.CardView
            android:layout_width="40dp"
            android:layout_height="40dp"
            app:cardElevation="0dp"
            app:cardCornerRadius="4dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rounded_user_image"
        android:scaleType="fitXY"/>

</android.support.v7.widget.CardView>

You can set your desired width, height and radius on CardView and scaleType on ImageView.

With AndroidX, use <androidx.cardview.widget.CardView>


this approach worked in my case with androidx.cardview.widget.CardView
however, it works but here image will stretch because of scaleType="fitXY" and does not look proper @ThemBones
@WIZARD that's why I mentioned that you can set your desired scaleType on ImageView
didn't work for me, i'm using androidx.cardview.widget.CardView and scaleType centerCrop
doesn't work for me. imageview overlaps the rounded corners of cardview
C
CoolMind

I have done by Custom ImageView:

public class RoundRectCornerImageView extends ImageView {

    private float radius = 18.0f;
    private Path path;
    private RectF rect;

    public RoundRectCornerImageView(Context context) {
        super(context);
        init();
    }

    public RoundRectCornerImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RoundRectCornerImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        path = new Path();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        rect = new RectF(0, 0, this.getWidth(), this.getHeight());
        path.addRoundRect(rect, radius, radius, Path.Direction.CW);
        canvas.clipPath(path);
        super.onDraw(canvas);
    }
}

How to use:

<com.mypackage.RoundRectCornerImageView
     android:id="@+id/imageView"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@drawable/image"
     android:scaleType="fitXY" />

Output:

https://i.stack.imgur.com/IXnaf.png

Hope this would help you.


this makes image blur
works even with android:scaleType="centerCrop", simple, doesn't create new bitmap. Thanks!
@Hiren This solution works fine for ImageView with a background Image. But it doesn't work for ImageViews which has just background color and no Image. Can you please tell me why this happening ?
This solution works only for image set with android:background, but not android:src.
i have improved this code CircularImage
T
TheFlash

I found that both methods were very helpful in coming up with a working solution. Here is my composite version, that is pixel independent and allows you to have some square corners with the rest of the corners having the same radius (which is the usual use case). With thanks to both of the solutions above:

public static Bitmap getRoundedCornerBitmap(Context context, Bitmap input, int pixels , int w , int h , boolean squareTL, boolean squareTR, boolean squareBL, boolean squareBR  ) {

    Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
    Canvas canvas = new Canvas(output);
    final float densityMultiplier = context.getResources().getDisplayMetrics().density;

    final int color = 0xff424242;
    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, w, h);
    final RectF rectF = new RectF(rect);

    //make sure that our rounded corner is scaled appropriately
    final float roundPx = pixels*densityMultiplier;

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint);


    //draw rectangles over the corners we want to be square
    if (squareTL ){
        canvas.drawRect(0, h/2, w/2, h, paint);
    }
    if (squareTR ){
        canvas.drawRect(w/2, h/2, w, h, paint);
    }
    if (squareBL ){
        canvas.drawRect(0, 0, w/2, h/2, paint);
    }
    if (squareBR ){
        canvas.drawRect(w/2, 0, w, h/2, paint);
    }


    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(input, 0,0, paint);

    return output;
}

Also, I overrode ImageView to put this in so I could define it in xml. You may want to add in some of the logic that the super call makes here, but I've commented it as it's not helpful in my case.

    @Override
protected void onDraw(Canvas canvas) {
    //super.onDraw(canvas);
        Drawable drawable = getDrawable();

        Bitmap b =  ((BitmapDrawable)drawable).getBitmap() ;
        Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);

        int w = getWidth(), h = getHeight();


        Bitmap roundBitmap =  CropImageView.getRoundedCornerBitmap( getContext(), bitmap,10 , w, h , true, false,true, false);
        canvas.drawBitmap(roundBitmap, 0,0 , null);
}

Hope this helps!


pretty awesome, especially extending the ImageView
A simple way to keep the logic of the ImageView#onDraw() is to set the rounded corner bitmap to the drawable of the ImageView, and leave the super.onDraw() to draw the bitmap. I've created a class RoundedCornerImageView, and its example of usage is here. Please notice that the getRoundedCornerBitmap() I've used is not pixel independent.
Thanks for the RoundedCornerImageView. I used it but modified it to be pixel-density independent.
The source code to umba's RoundedCornerImageView is here: code.google.com/p/android-batsg/source/browse/trunk/…
@Caspar Harmer working good just 1 edit...there are four conditions..just change them as if I set true,true,false,false it will set bottom corner instead of top corner..otherwise code is working fine.so squareTL and squareTR are condtions for squareBL and squareBR respectively..and vica-versa.
L
LaurentY

Rounded image Using ImageLoader here

Create DisplayImageOptions:

DisplayImageOptions options = new DisplayImageOptions.Builder()
    // this will make circle, pass the width of image 
    .displayer(new RoundedBitmapDisplayer(getResources().getDimensionPixelSize(R.dimen.image_dimen_menu))) 
    .cacheOnDisc(true)
    .build();

imageLoader.displayImage(url_for_image,ImageView,options);

Or you can user Picasso Library from Square.

Picasso.with(mContext)
    .load(com.app.utility.Constants.BASE_URL+b.image)
    .placeholder(R.drawable.profile)
    .error(R.drawable.profile)
    .transform(new RoundedTransformation(50, 4))
    .resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)
    .centerCrop()
    .into(v.im_user);

you can download RoundedTransformation file here here


The Picasso Library one is good, and very easy to implement, thanks a lot +1
The Picasso library seems to not transform the "placeholder" and "error" image though, so if your image fails to load (error) or takes a while to load initially (placeholder) it won't display the image as a rounded image. github.com/square/picasso/issues/337
Other useful picasso transformations are provided by this library, which also contains a RoundedCornersTransformation: github.com/wasabeef/picasso-transformations
Doesn't work with cropping images in Univ. Image Loader.
C
Christian

As all the answers seemed too complicated for me just for round corners I thought and came to another solution which I think is worth to share, just with XML in case you have some space around the image:

Create a bordered shape with transparent content like this:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners 
        android:radius="30dp" />
    <stroke 
        android:color="#ffffffff"
        android:width="10dp" />
</shape> 

Then in a RelativeLayout you can first place your image and then in the same location above the shape with another ImageView. The cover-shape should be larger in size by the amount of the border width. Be careful to take a larger corner radius as the outer radius is defined but the inner radius is what covers your image.

Hope it helps somebody, too.

Edit as per CQM request the relative layout example:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageToShow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/imgCorners"
        android:layout_alignLeft="@+id/imgCorners"
        android:layout_alignRight="@+id/imgCorners"
        android:layout_alignTop="@+id/imgCorners"
        android:background="#ffffff"
        android:contentDescription="@string/desc"
        android:padding="5dp"
        android:scaleType="centerCrop" />

    <ImageView
        android:id="@+id/imgCorners"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:contentDescription="@string/desc"
        android:src="@drawable/corners_white" />

</RelativeLayout>

can you elaborate more on this with more code, especially what is being done with the second image view and where the "bordered shape" xml is being applied (as src or as background?) several questions here. I want to like this solution since it will let me control all four corners independently
Although seems to be a simple answer, it unfortunately adds margin around the "masked" image view which is undesirable side effect.
yes, but the same works with an ImageView based on a PNG-Drawable with rounded corners and transparent background or a 9-Patch-Drawable if you have an undefined aspect.
I am doing the same way, and its working; just willing to know if it will not create any problem with orientation and different phone size?
If you keep the dimensions relative to eachother this should not create a problem.
F
Flexo

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);

    }

}

Where does ImageUtilities come from?
@JasonWyatt see sorrodos post below
C
CoolMind

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);

.roundedCorners not coming do we need to setup anything else? Even after rebuilding project
This worked for me, thank you. I found it simpler to avoid using the @GlideExtension annotated class and just went with .transform(new RoundedCorners(px)) where px is the same (int px = Math.round(cornerRadius * (context.getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));).
g
grrigore

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:

https://i.stack.imgur.com/93MKB.png


B
Badr At

Here is a simple example overriding imageView, you can then also use it in layout designer to preview.

public class RoundedImageView extends ImageView {

    public RoundedImageView(Context context) {
        super(context);
    }

    public RoundedImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RoundedImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        float radius = 0.1f;
        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        RoundedBitmapDrawable rid = RoundedBitmapDrawableFactory.create(getResources(), bitmap);
        rid.setCornerRadius(bitmap.getWidth() * radius);
        super.setImageDrawable(rid);
    }
}

This is for fast solution. Radius is used on all corners and is based of percentage of bitmap width.

I just overrided setImageDrawable and used support v4 method for rounded bitmap drawable.

Usage:

<com.example.widgets.RoundedImageView
        android:layout_width="39dp"
        android:layout_height="39dp"
        android:src="@drawable/your_drawable" />

Preview with imageView and custom imageView:

https://i.stack.imgur.com/Ae9E8.png


The app crashes if the image is being set dynamically with Glide.
M
Martin Zeitler

It can be done with a ShapeableImageView using a ShapeAppearanceOverlay:

<com.google.android.material.imageview.ShapeableImageView
    android:id="@+id/avatar"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:padding="4dp"
    app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"/>

Where style ShapeAppearanceOverlay.Avatar resides in res/values/styles.xml:

<style name="ShapeAppearanceOverlay.Avatar" parent="ShapeAppearance.MaterialComponents.SmallComponent">
    <item name="cornerFamily">rounded</item>
    <item name="cornerSize">50%</item>
</style>

This just need equal layout_height and layout_width set, else with will be a pill an no circle.


V
Vahid

Kotlin

import android.graphics.BitmapFactory
import android.os.Bundle
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
import kotlinx.android.synthetic.main.activity_main.*

val bitmap = BitmapFactory.decodeResource(resources, R.drawable.myImage)
val rounded = RoundedBitmapDrawableFactory.create(resources, bitmap)
rounded.cornerRadius = 20f
profileImageView.setImageDrawable(rounded)

To make ImageView Circular we can change cornerRadius with:

rounded.isCircular = true

Highly underrated answer. Clear and simple to implement. Should be the solution.
T
Trikaldarshiii

Apply a shape to your imageView as below:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <solid android:color="#faf5e6" />
    <stroke
        android:width="1dp"
        android:color="#808080" />
    <corners android:radius="15dp" />
    <padding
        android:bottom="5dp"
        android:left="5dp"
        android:right="5dp"
        android:top="5dp" />
</shape>

it may be helpful to you friend.


M
MrSnowflake

You should extend ImageView and draw your own rounded rectangle.

If you want a frame around the image you could also superimpose the rounded frame on top of the image view in the layout.

[edit]Superimpose the frame on to op the original image, by using a FrameLayout for example. The first element of the FrameLayout will be the image you want to diplay rounded. Then add another ImageView with the frame. The second ImageView will be displayed on top of the original ImageView and thus Android will draw it's contents above the orignal ImageView.


Thank you. But there is only setDrawable method for ImageView, how can I setDrawable of the ImageView to the content of my image and then superimpose a rounded frame on top of the ImageView?
I'm sorry, I was being unclear. I meant superimposing in the layout: thus (ie) use a FrameLayout put an ImageView in it and add another ImageView with the rounded frame. That way the first ImageView will display your selected picture and the second ImageFrame will display the rounded frame.
Correct - with FrameLayout you can overlay one image/view with another. You can also make use of the android:foreground tag of FrameLayout.
s
sorrodos

Props to George Walters II above, I just took his answer and extended it a bit to support rounding individual corners differently. This could be optimized a bit further (some of the target rects overlap), but not a whole lot.

I know this thread is a bit old, but its one of the top results for queries on Google for how to round corners of ImageViews on Android.

/**
 * Use this method to scale a bitmap and give it specific rounded corners.
 * @param context Context object used to ascertain display density.
 * @param bitmap The original bitmap that will be scaled and have rounded corners applied to it.
 * @param upperLeft Corner radius for upper left.
 * @param upperRight Corner radius for upper right.
 * @param lowerRight Corner radius for lower right.
 * @param lowerLeft Corner radius for lower left.
 * @param endWidth Width to which to scale original bitmap.
 * @param endHeight Height to which to scale original bitmap.
 * @return Scaled bitmap with rounded corners.
 */
public static Bitmap getRoundedCornerBitmap(Context context, Bitmap bitmap, float upperLeft,
        float upperRight, float lowerRight, float lowerLeft, int endWidth,
        int endHeight) {
    float densityMultiplier = context.getResources().getDisplayMetrics().density;

    // scale incoming bitmap to appropriate px size given arguments and display dpi
    bitmap = Bitmap.createScaledBitmap(bitmap, 
            Math.round(endWidth * densityMultiplier),
            Math.round(endHeight * densityMultiplier), true);

    // create empty bitmap for drawing
    Bitmap output = Bitmap.createBitmap(
            Math.round(endWidth * densityMultiplier),
            Math.round(endHeight * densityMultiplier), Config.ARGB_8888);

    // get canvas for empty bitmap
    Canvas canvas = new Canvas(output);
    int width = canvas.getWidth();
    int height = canvas.getHeight();

    // scale the rounded corners appropriately given dpi
    upperLeft *= densityMultiplier;
    upperRight *= densityMultiplier;
    lowerRight *= densityMultiplier;
    lowerLeft *= densityMultiplier;

    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setColor(Color.WHITE);

    // fill the canvas with transparency
    canvas.drawARGB(0, 0, 0, 0);

    // draw the rounded corners around the image rect. clockwise, starting in upper left.
    canvas.drawCircle(upperLeft, upperLeft, upperLeft, paint);
    canvas.drawCircle(width - upperRight, upperRight, upperRight, paint);
    canvas.drawCircle(width - lowerRight, height - lowerRight, lowerRight, paint);
    canvas.drawCircle(lowerLeft, height - lowerLeft, lowerLeft, paint);

    // fill in all the gaps between circles. clockwise, starting at top.
    RectF rectT = new RectF(upperLeft, 0, width - upperRight, height / 2);
    RectF rectR = new RectF(width / 2, upperRight, width, height - lowerRight);
    RectF rectB = new RectF(lowerLeft, height / 2, width - lowerRight, height);
    RectF rectL = new RectF(0, upperLeft, width / 2, height - lowerLeft);

    canvas.drawRect(rectT, paint);
    canvas.drawRect(rectR, paint);
    canvas.drawRect(rectB, paint);
    canvas.drawRect(rectL, paint);

    // set up the rect for the image
    Rect imageRect = new Rect(0, 0, width, height);

    // set up paint object such that it only paints on Color.WHITE
    paint.setXfermode(new AvoidXfermode(Color.WHITE, 255, AvoidXfermode.Mode.TARGET));

    // draw resized bitmap onto imageRect in canvas, using paint as configured above
    canvas.drawBitmap(bitmap, imageRect, imageRect, paint);

    return output;
}

+1 for adding in the density multiplier and adding support for individually rounding corners. I actually used the solution at the top, as your solution didn't quite work - but it was very helpful! See my composite solution below:
A
Alex

Romain Guy is where it's at.

Minified version as follows.

Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.image)).getBitmap();

Bitmap bitmapRounded = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
Canvas canvas = new Canvas(bitmapRounded);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
canvas.drawRoundRect((new RectF(0.0f, 0.0f, bitmap.getWidth(), bitmap.getHeight())), 10, 10, paint);

imageView.setImageBitmap(bitmapRounded);

sadly, even though it works, but just like the rest of the solutions here, it creates a new bitmap instead of using the current one.
not really. bitmapRounded would be used again and again. if you had access to the drawable canvas, you can use the draw method directly instead of generating a new bitmap.
how do you do that? suppose i don't use any special drawable and only handle a single bitmap and use setImageBitmap in the end of the process. how would i achieve such a thing?
both don't change the bitmap , but wrap it using a custom drawable and a custom imageview. this isn't what i asked about. i asked how do you change the bitmap itself.
M
Max Base

Why not do clipping in draw()?

Here is my solution:

Extend RelativeLayout with clipping

Put ImageView (or other views) into the layout:

Code:

public class RoundRelativeLayout extends RelativeLayout {

    private final float radius;

    public RoundRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray attrArray = context.obtainStyledAttributes(attrs,
                R.styleable.RoundRelativeLayout);
        radius = attrArray.getDimension(
                R.styleable.RoundRelativeLayout_radius, 0);
    }

    private boolean isPathValid;
    private final Path path = new Path();

    private Path getRoundRectPath() {
        if (isPathValid) {
            return path;
        }

        path.reset();

        int width = getWidth();
        int height = getHeight();
        RectF bounds = new RectF(0, 0, width, height);

        path.addRoundRect(bounds, radius, radius, Direction.CCW);
        isPathValid = true;
        return path;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.clipPath(getRoundRectPath());
        super.dispatchDraw(canvas);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.clipPath(getRoundRectPath());
        super.draw(canvas);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int oldWidth = getMeasuredWidth();
        int oldHeight = getMeasuredHeight();
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int newWidth = getMeasuredWidth();
        int newHeight = getMeasuredHeight();
        if (newWidth != oldWidth || newHeight != oldHeight) {
            isPathValid = false;
        }
    }

}

This doesn't work with hardward acceleration turned on, correct? I didn't see an easy workaround...
M
Max Base

This pure xml solution was good enough in my case. http://www.techrepublic.com/article/pro-tip-round-corners-on-an-android-imageview-with-this-hack/

EDIT

Here's the answer in a nutshell:

In the /res/drawable folder, create a frame.xml file. In it, we define a simple rectangle with rounded corners and a transparent center.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
     <solid android:color="#00ffffff" />
     <padding android:left="6dp"
        android:top="6dp"
        android:right="6dp"
        android:bottom="6dp" />
     <corners android:radius="12dp" />
     <stroke android:width="6dp" android:color="#ffffffff" />
</shape>

In your layout file you add a LinearLayout that contains a standard ImageView, as well as a nested FrameLayout. The FrameLayout uses padding and the custom drawable to give the illusion of rounded corners.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_gravity="center"
    android:gravity="center" 
    android:background="#ffffffff">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="6dp"
        android:src="@drawable/tr"/>

    <FrameLayout 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="6dp"
            android:src="@drawable/tr"/>

        <ImageView 
             android:src="@drawable/frame"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />

    </FrameLayout>

</LinearLayout>

F
Fattie

None of the methods provided in the answers worked for me. I found the following way works if your android version is 5.0 or above:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

    ViewOutlineProvider provider = new ViewOutlineProvider() {
        @Override
        public void getOutline(View view, Outline outline) {
            int curveRadius = 24;
            outline.setRoundRect(0, 0, view.getWidth(), (view.getHeight()+curveRadius), curveRadius);
        }
    };
    imageview.setOutlineProvider(provider);
    imageview.setClipToOutline(true);
}

No xml shapes to be defined, and the code above create corners only for top, which normal methods won't work. If you need 4 corners to be rounded, remove:

"+ curveRadius"  

From the parameter for bottom in setRoundRect. You can further expand the shape to any others by specifying outlines that suit your needs. Check out the following link:

Android Developer Documentation.

Note, as with any measure in Android, you have to "convert" the size typically from DP. In the example above, say you want the radius to be 24

                            int curveRadius = 24;

For example you may be later adding a border in a drawable with the radius set as "24" and you wish it to match. Hence,

    float desiredRadius = 24;
    float radiusConverted = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            desiredRadius,
            itemView.getContext().getResources().getDisplayMetrics());

and then

                            int curveRadius = radiusConverted;

AHHHHH .. multiply by the screen density !
dear @us_david , regarding your amazing answer, I added a note about getting the radius correct to match other radius in the app. obviously delete or edit as you seer fit! thank you so much for the answer !!!
D
Danny Remington - OMS

The following creates a rounded rectangle layout object that draws a rounded rectangle around any child objects that are placed in it. It also demonstrates how to create views and layouts programmatically without using the layout xml files.

package android.example;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MessageScreen extends Activity {
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  int mainBackgroundColor = Color.parseColor("#2E8B57");
  int labelTextColor = Color.parseColor("#FF4500");
  int messageBackgroundColor = Color.parseColor("#3300FF");
  int messageTextColor = Color.parseColor("#FFFF00");

  DisplayMetrics metrics = new DisplayMetrics();
  getWindowManager().getDefaultDisplay().getMetrics(metrics);
  float density = metrics.density;
  int minMarginSize = Math.round(density * 8);
  int paddingSize = minMarginSize * 2;
  int maxMarginSize = minMarginSize * 4;

  TextView label = new TextView(this);
  /*
   * The LayoutParams are instructions to the Layout that will contain the
   * View for laying out the View, so you need to use the LayoutParams of
   * the Layout that will contain the View.
   */
  LinearLayout.LayoutParams labelLayoutParams = new LinearLayout.LayoutParams(
    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  label.setLayoutParams(labelLayoutParams);
  label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
  label.setPadding(paddingSize, paddingSize, paddingSize, paddingSize);
  label.setText(R.string.title);
  label.setTextColor(labelTextColor);

  TextView message = new TextView(this);
  RoundedRectangle.LayoutParams messageLayoutParams = new RoundedRectangle.LayoutParams(
 LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
  /*
   * This is one of the calls must made to force a ViewGroup to call its
   * draw method instead of just calling the draw method of its children.
   * This tells the RoundedRectangle to put some extra space around the
   * View.
   */
  messageLayoutParams.setMargins(minMarginSize, paddingSize,
    minMarginSize, maxMarginSize);
  message.setLayoutParams(messageLayoutParams);
  message.setTextSize(TypedValue.COMPLEX_UNIT_SP, paddingSize);
  message.setText(R.string.message);
  message.setTextColor(messageTextColor);
  message.setBackgroundColor(messageBackgroundColor);

  RoundedRectangle messageContainer = new RoundedRectangle(this);
  LinearLayout.LayoutParams messageContainerLayoutParams = new LinearLayout.LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
  messageContainerLayoutParams.setMargins(paddingSize, 0, paddingSize, 0);
  messageContainer.setLayoutParams(messageContainerLayoutParams);
  messageContainer.setOrientation(LinearLayout.VERTICAL);
  /*
   * This is one of the calls must made to force a ViewGroup to call its
   * draw method instead of just calling the draw method of its children.
   * This tells the RoundedRectangle to color the the exta space that was
   * put around the View as well as the View. This is exterior color of
   * the RoundedRectangle.
   */
  messageContainer.setBackgroundColor(mainBackgroundColor);
  /*
   * This is one of the calls must made to force a ViewGroup to call its
   * draw method instead of just calling the draw method of its children.
   * This is the interior color of the RoundedRectangle. It must be
   * different than the exterior color of the RoundedRectangle or the
   * RoundedRectangle will not call its draw method.
   */
  messageContainer.setInteriorColor(messageBackgroundColor);
  // Add the message to the RoundedRectangle.
  messageContainer.addView(message);

  //
  LinearLayout main = new LinearLayout(this);
  LinearLayout.LayoutParams mainLayoutParams = new LinearLayout.LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
  main.setLayoutParams(mainLayoutParams);
  main.setOrientation(LinearLayout.VERTICAL);
  main.setBackgroundColor(mainBackgroundColor);
  main.addView(label);
  main.addView(messageContainer);

  setContentView(main);
 }
}

The class for RoundedRectangle layout object is as defined here:

/**
 *  A LinearLayout that draws a rounded rectangle around the child View that was added to it.
 */
package android.example;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.widget.LinearLayout;

/**
 * A LinearLayout that has rounded corners instead of square corners.
 * 
 * @author Danny Remington
 * 
 * @see LinearLayout
 * 
 */
public class RoundedRectangle extends LinearLayout {
 private int mInteriorColor;

 public RoundedRectangle(Context p_context) {
  super(p_context);
 }

 public RoundedRectangle(Context p_context, AttributeSet attributeSet) {
  super(p_context, attributeSet);
 }

 // Listener for the onDraw event that occurs when the Layout is drawn.
 protected void onDraw(Canvas canvas) {
  Rect rect = new Rect(0, 0, getWidth(), getHeight());
  RectF rectF = new RectF(rect);
  DisplayMetrics metrics = new DisplayMetrics();
  Activity activity = (Activity) getContext();
  activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
  float density = metrics.density;
  int arcSize = Math.round(density * 10);

  Paint paint = new Paint();
  paint.setColor(mInteriorColor);

  canvas.drawRoundRect(rectF, arcSize, arcSize, paint);
 }

 /**
  * Set the background color to use inside the RoundedRectangle.
  * 
  * @param Primitive int - The color inside the rounded rectangle.
  */
 public void setInteriorColor(int interiorColor) {
  mInteriorColor = interiorColor;
 }

 /**
  * Get the background color used inside the RoundedRectangle.
  * 
  * @return Primitive int - The color inside the rounded rectangle.
  */
 public int getInteriorColor() {
  return mInteriorColor;
 }

}

י
ישו אוהב אותך

If you are using Glide Library this would be helpful:

Glide.with(getApplicationContext())
     .load(image_url)
     .asBitmap()
     .centerCrop()
     .into(new BitmapImageViewTarget(imageView) {
        @Override
        protected void setResource(Bitmap resource) {
          RoundedBitmapDrawable circularBitmapDrawable =
                       RoundedBitmapDrawableFactory.create(getApplicationContext().getResources(), resource);
          circularBitmapDrawable.setCornerRadius(dpToPx(10));
          circularBitmapDrawable.setAntiAlias(true);
          imageView.setImageDrawable(circularBitmapDrawable);
        }
     });


public int dpToPx(int dp) {
  DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics();
  return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

If using Glide version 4 and above use --- RequestOptions requestOptions = new RequestOptions(); requestOptions = requestOptions.transforms(new CenterCrop(), new RoundedCorners(16)); Glide.with(itemView.getContext()) .load(item.getImage()) .apply(requestOptions) .into(mProgramThumbnail);
@B.shruti, probably it doesn't work. See more fresh solutions.
Anirudh, wow, it works for rectangular images! I converted to Kotlin, and it works for Glide 4. In this case .asBitmap() should be inserted before .load(image_url).
m
mkm

Thanks a lot to first answer. Here is modified version to convert a rectangular image into a square one (and rounded) and fill color is being passed as parameter.

public static Bitmap getRoundedBitmap(Bitmap bitmap, int pixels, int color) {

    Bitmap inpBitmap = bitmap;
    int width = 0;
    int height = 0;
    width = inpBitmap.getWidth();
    height = inpBitmap.getHeight();

    if (width <= height) {
        height = width;
    } else {
        width = height;
    }

    Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    Canvas canvas = new Canvas(output);

    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, width, height);
    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(inpBitmap, rect, rect, paint);

    return output;
}

A
Amir Ziarati

if your image is on internet the best way is using glide and RoundedBitmapDrawableFactory (from API 21 - but available in support library) like so:

 Glide.with(ctx).load(url).asBitmap().centerCrop().into(new BitmapImageViewTarget(imageView) {
    @Override
    protected void setResource(Bitmap res) {
        RoundedBitmapDrawable bitmapDrawable =
             RoundedBitmapDrawableFactory.create(ctx.getResources(), res);
        bitmapDrawable.setCircular(true);//comment this line and uncomment the next line if you dont want it fully cricular
        //circularBitmapDrawable.setCornerRadius(cornerRadius);
        imageView.setImageDrawable(bitmapDrawable);
    }
});

This is an elegant api, however, .centerInside seems to be missing, hence it is perhaps better to use xml to define such parameters
This is also useful when image is in resources and you need to use centerCrop
D
Deep Patel

you can use only ImageView in your layout and using glide, you can apply round corners using this method.

first in your gradle write,

compile 'com.github.bumptech.glide:glide:3.7.0'

for image with rounded corners,

public void loadImageWithCorners(String url, ImageView view) {
    Glide.with(context)
            .load(url)
            .asBitmap()
            .centerCrop()
            .placeholder(R.color.gray)
            .error(R.color.gray)
            .diskCacheStrategy(DiskCacheStrategy.SOURCE)
            .into(new BitmapImageViewTarget(view) {
                @Override
                protected void setResource(Bitmap resource) {
                    RoundedBitmapDrawable circularBitmapDrawable =
                            RoundedBitmapDrawableFactory.create(context.getResources(), resource);
                    circularBitmapDrawable.setCornerRadius(32.0f); // radius for corners
                    view.setImageDrawable(circularBitmapDrawable);
                }
            });
}

call method :

loadImageWithCorners("your url","your imageview");

This will add a library for loading images through HTTP request, which is not relevant to the question.
Which library will be loaded? For image loading you can use glide and the snippet it purely based on methods and classes of glide itself. No other library needs to be added
Glide is a library primarily for loading images over HTTP, but OP wanted to know how to round the corners of an ImageView.
M
Max Base

By using below code you can change top corner radius

val image = findViewById<ImageView>(R.id.image)
val curveRadius = 20F

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

    image.outlineProvider = object : ViewOutlineProvider() {

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun getOutline(view: View?, outline: Outline?) {
            outline?.setRoundRect(0, 0, view!!.width, (view.height+curveRadius).toInt(), curveRadius)
        }
    }

    image.clipToOutline = true

}

For android it would be like this: float curveRadius = 20F; ViewOutlineProvider outlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { outline.setRoundRect(0, 0, view.getWidth(), (int) (view.getHeight()+curveRadius), curveRadius); } }; imatge.setOutlineProvider(outlineProvider); imatge.setClipToOutline(true);