I need to create a rectangle view with two rounded corners using canvas.I used drawRoundRect but i am getting rectangle with four rounded corners.Please anyone suggest me a way that can be helpful in solving my problem.
rect = new RectF(left, top, right, bottom);
canvas.drawRoundRect(rect, 20, 20, facePaint);
A bit wasteful but first draw a rounded rectangle with all 4 corners rounded, then draw second, regular rectangle radius height below to draw over the bottom rounded corners
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
float radius = 20f;
#Override
public void onDraw(Canvas canvas) {
// Draw the top part that has rounded corners with twice the height of the radius
canvas.drawRoundRect(0f, 0f, 100f, 2 * radius, radius, radius, paint);
// Draw the bottom part, partly on top of the top part
canvas.drawRect(0f, radius, 100f, 100f, paint);
}
A tiny bit of math needed to account for the desired height but shouldn't be that hard :)
There is no built in function to do that. You'll need to create a Path with the shape you want, then use drawPath to draw the path to the canvas
Simply go to you drawable folder add new xml file and add below code:-
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="20dp"/>
<solid android:color="#color/green" />
</shape>
After that set background of any view like this:-
android:background="#drawable/round_green"
you can change color according to your choice.
Hope it will helps you.
Use this :-
// Initialize a new Bitmap
Bitmap bitmap = Bitmap.createBitmap(
600, // Width
300, // Height
Bitmap.Config.ARGB_8888 // Config
);
// Initialize a new Canvas instance
Canvas canvas = new Canvas(bitmap);
// Draw a solid color on the canvas as background
canvas.drawColor(Color.WHITE);
// Initialize a new Paint instance to draw the rounded rectangle
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.RED);
paint.setAntiAlias(true);
// Set an offset value in pixels to draw rounded rectangle on canvas
int offset = 50;
/*
public RectF (float left, float top, float right, float bottom)
Create a new rectangle with the specified coordinates. Note: no range
checking is performed, so the caller must ensure that
left <= right and top <= bottom.
Parameters
left The X coordinate of the left side of the rectangle
top The Y coordinate of the top of the rectangle
right The X coordinate of the right side of the rectangle
bottom The Y coordinate of the bottom of the rectangle
*/
// Initialize a new RectF instance
RectF rectF = new RectF(
offset, // left
offset, // top
canvas.getWidth() - offset, // right
canvas.getHeight() - offset // bottom
);
/*
public void drawRoundRect (RectF rect, float rx, float ry, Paint paint)
Draw the specified round-rect using the specified paint. The roundrect
will be filled or framed based on the Style in the paint.
Parameters
rect : The rectangular bounds of the roundRect to be drawn
rx : The x-radius of the oval used to round the corners
ry : The y-radius of the oval used to round the corners
paint : The paint used to draw the roundRect
*/
// Define the corners radius of rounded rectangle
int cornersRadius = 25;
// Finally, draw the rounded corners rectangle object on the canvas
canvas.drawRoundRect(
rectF, // rect
cornersRadius, // rx
cornersRadius, // ry
paint // Paint
);
// Display the newly created bitmap on app interface
mImageView.setImageBitmap(bitmap);
The source of drawRoundRect .So if you want to draw two round corners .
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
And you can try this .Just left and top have round corners.
canvas.drawRoundRect(10,10,0,0,20,20,facePaint);
You can make 2 corners rounded rectangle with xml drawable.
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:bottomLeftRadius="30dp" android:bottomRightRadius="30dp"></corners>
<solid android:color="#color/white"></solid>
</shape>
Background
I've already seen how to create a drawable that's circular out of a bitmap, and also how to add an outline (AKA stroke) around it, here.
The problem
I can't find out how to do a similar task for rounding only some of the corners of the bitmap, inside the drawable, without creating a new bitmap, and still do it for a center-crop ImageView.
What I've found
This is what I've found, but it does create a new bitmap, and when using it in an imageView with center-crop (source here):
/**
* Create rounded corner bitmap from original bitmap.
*
* #param input Original bitmap.
* #param cornerRadius Corner radius in pixel.
* #param squareTL,squareTR,squareBL,squareBR where to use square corners instead of rounded ones.
*/
public static Bitmap getRoundedCornerBitmap(final Bitmap input, final float cornerRadius, final int w, final int h,
final boolean squareTL, final boolean squareTR, final boolean squareBL, final boolean squareBR) {
final Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Rect rect = new Rect(0, 0, w, h);
final RectF rectF = new RectF(rect);
// make sure that our rounded corner is scaled appropriately
Paint paint = new Paint();
paint.setXfermode(null);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
// draw rectangles over the corners we want to be square
if (squareTL)
canvas.drawRect(0, 0, w / 2, h / 2, paint);
if (squareTR)
canvas.drawRect(w / 2, 0, w, h / 2, paint);
if (squareBL)
canvas.drawRect(0, h / 2, w / 2, h, paint);
if (squareBR)
canvas.drawRect(w / 2, h / 2, w, h, paint);
paint.setXfermode(PORTER_DUFF_XFERMODE_SRC_IN);
canvas.drawBitmap(input, 0, 0, paint);
return output;
}
And, this is what I've found for creating a rounded corners drawable that acts on all corners:
public static class RoundedCornersDrawable extends Drawable {
private final float mCornerRadius;
private final RectF mRect = new RectF();
private final BitmapShader mBitmapShader;
private final Paint mPaint;
public RoundedCornersDrawable(final Bitmap bitmap, final float cornerRadius) {
mCornerRadius = cornerRadius;
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(false);
mPaint.setShader(mBitmapShader);
mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(0, 0, bounds.width(), bounds.height());
}
#Override
public void draw(final Canvas canvas) {
canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}
But this solution only works well if the imageView shows the content while maintaining the same aspect ratio as the bitmap, and also has its size pre-determined.
The question
How to create a center-cropped drawable, that shows a bitmap, has rounded corners for specific corners, and also be able to show an outline/stroke around it?
I want to do it without creating a new bitmap or extending ImageView. Only use a drawable that has the bitmap as the input.
The SMART way is to use the PorterDuff blending mode. It's really simple and slick to create any fancy shading, blending, "crop" effect. you can find a lot of good tutorial about PorterDuff. here a good one.
I always use this library to achieve what you are looking for. you can round any corner you want and also add stroke.
https://github.com/vinc3m1/RoundedImageView
You can use it or see it's source codes just for inspiration.
EDIT
there is no need to use Image View and make bitmap or drawable yourself and show it in Image View.
Just replace Image View with Rounded Image View and it will handle everything for you without any extra work in code !
here is sample :
<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageView1"
android:scaleType="centerCrop"
app:riv_corner_radius="8dp"
app:riv_border_width="2dp"
app:riv_border_color="#333333"
app:riv_oval="false" />
In code, just pass any image resource to it or use any Image Loader with it.
RoundedImageView myRoundedImage=(RoundedImageView)findViewById(R.id.imageView1);
myRoundedImage.setImageResource(R.drawable.MY_DRAWABLE);
// OR
ImageLoader.getInstance().displayImage(YOUR_IMAGE_URL, myRoundedImage);
if you want to just make specific corners rounded try this:
<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageView1"
android:scaleType="centerCrop"
app:riv_corner_radius_top_right="8dp"
app:riv_corner_radius_bottom_right="8dp"
app:riv_border_width="2dp"
app:riv_border_color="#333333"
app:riv_oval="false" />
Well, you can create a new .xml drawable named my_background and paste this code below:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<solid android:color="#00000000"/>
<corners
android:bottomLeftRadius="12dp"
android:bottomRightRadius="12dp"
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
<stroke
android:width="1dp"
android:color="#000000"
/>
</shape>
Then, you set your ImageButton's, ImageView's background to your new drawable, like this:
android:background="#drawable/my_background"
android:scaleType="centerCrop"
or programatically:
myView.setBackground(R.drawable.my_background);
Hope it helps!
EDIT:
To programmatically create a similar drawable, you can use it:
GradientDrawable shape = new GradientDrawable();
shape.setShape(GradientDrawable.RECTANGLE);
shape.setCornerRadii(new float[] { 8, 8, 8, 8, 0, 0, 0, 0 });
shape.setColor(Color.TRANSPARENT);
shape.setStroke(3, Color.BLACK);
v.setBackgroundDrawable(shape);
Ok, here's my try. The only gotcha is that "int corners" is meant to be a set of flags. Such as 0b1111 where each 1 represents a corner to be rounded, and 0 is the opposite. The order is TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT.
example usage first, formatted for readability:
((ImageView) findViewById(R.id.imageView)).setImageDrawable(
new RoundedRectDrawable(
getResources(),
bitmap,
(float) .15,
0b1101,
8,
Color.YELLOW
)
);
code:
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.SystemClock;
/**
* Created by mliu on 4/15/16.
*/
public class RoundedRectDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
private final float outlineBorderRadius;
private final int w;
private final int h;
private final int corners;
private final int border;
private final int bordercolor;
public RoundedRectDrawable(final Resources resources, final Bitmap bitmap, float borderRadiusSeed, int corners, int borderPX, int borderColor) {
super(resources, bitmap);
bitmapShader = new BitmapShader(getBitmap(),
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
final Bitmap b = getBitmap();
p = getPaint();
p.setAntiAlias(true);
p.setShader(bitmapShader);
w = b.getWidth();
h = b.getHeight();
rect = new RectF(0,0,w,h);
borderRadius = borderRadiusSeed * Math.min(w, h);
border = borderPX;
this.corners = corners;
this.bordercolor = borderColor;
outlineBorderRadius = borderRadiusSeed * Math.min(w+border,h+border);
}
#Override
public void draw(final Canvas canvas) {
if ((corners&0b1111)==0){
if (border>0) {
Paint border = new Paint();
border.setColor(bordercolor);
canvas.drawRect(rect, border);
}
canvas.drawRect(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border, p);
} else {
if (border >0) {
Paint border = new Paint();
border.setColor(bordercolor);
canvas.drawRoundRect(rect, outlineBorderRadius, outlineBorderRadius, border);
if ((corners & 0b1000) == 0) {
//top left
canvas.drawRect(rect.left, rect.top, rect.width() / 2, rect.height() / 2, border);
}
if ((corners & 0b0100) == 0) {
//top right
canvas.drawRect(rect.width() / 2, rect.top, rect.width(), rect.height() / 2, border);
}
if ((corners & 0b0010) == 0) {
//bottom left
canvas.drawRect(rect.left, rect.height() / 2, rect.width() / 2, rect.height(), border);
}
if ((corners & 0b0001) == 0) {
//bottom right
canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width(), rect.height(), border);
}
}
canvas.drawRoundRect(new RectF(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border), borderRadius, borderRadius, p);
if ((corners & 0b1000) == 0) {
//top left
canvas.drawRect(rect.left + border, rect.top + border, rect.width() / 2, rect.height() / 2, p);
}
if ((corners & 0b0100) == 0) {
//top right
canvas.drawRect(rect.width() / 2, rect.top + border, rect.width() - border, rect.height() / 2, p);
}
if ((corners & 0b0010) == 0) {
//bottom left
canvas.drawRect(rect.left + border, rect.height() / 2, rect.width() / 2, rect.height() - border, p);
}
if ((corners & 0b0001) == 0) {
//bottom right
canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width() - border, rect.height() - border, p);
}
}
}
}
So, this handles the outline first, if needed, then the bitmap. It marks the canvas up with a rounded rect first, then "squares out" each corner you don't want to round. Seems highly inefficient, and probably is, but average case run time before minimal optimizations (corners = 0b0000, 10 canvas.draw calls) takes ~ 200us on a S7. And, that time is SUPER inconsistent based on phone usage. I've gotten as low as 80us and as high as 1.5ms.
NOTES/WARNING: I am BAD at this. This is not optimal. There's probably better answers already here on SO. The subject matter is just a bit uncommon and difficult to search up. I was originally not going to post this, but at time of writing this is still not marked answered, and the library OP did not wish to use due to problems with their Drawable actually use a very similar approach as my terrible solution. So, now I'm less embarrassed to share this. Additionally, though what I posted today was 95% written yesterday, I know I got some of this code from a tutorial or a SO post, but I can't remember who to attribute cause I didn't end up using it in my project. Apologies whoever you are.
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
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;
}
}
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>
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:
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.
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.
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());
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))
)
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);
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>
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:
Hope this would help you.
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!
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
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>
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.
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);
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:
Try the Material Components Library and use the ShapeableImageView.
Somethig like this :
Java :
imageView=new ShapeableImageView(context);
imageView.setShapeAppearanceModel(
imageView.getShapeAppearanceModel()
.toBuilder()
.setAllCornerSizes(20)
.build());
Kotlin :
val imageView = ShapeableImageView(context)
imageView.setShapeAppearanceModel(
imageView.getShapeAppearanceModel()
.toBuilder()
.setAllCornerSizes(20f)
.build())
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:
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
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.
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.
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;
}
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);
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 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>
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;
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));
}
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;
}
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);
}
});
Answer for the question that is redirected here:
"How to create a circular ImageView in Android?"
public static Bitmap getRoundBitmap(Bitmap bitmap) {
int min = Math.min(bitmap.getWidth(), bitmap.getHeight());
Bitmap bitmapRounded = Bitmap.createBitmap(min, min, 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, min, min)), min/2, min/2, paint);
return bitmapRounded;
}
I am working on creating a custom ImageView which will crop my image into a hexagon shape and add a border. I was wondering if my approach is correct or if I am doing this the wrong way. There are a bunch of custom libraries out there that already do this but none of them out of the box have the shape I am looking for. That being said, this is more a question about the best practice.
You can see the full class in this gist, but the main question is that is this the best approach. It feels wrong to me, partly because of some of the magic numbers which means it could be messed up on some devices.
Here is the meat of the code:
#Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null || getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
int dimensionPixelSize = getResources().getDimensionPixelSize(R.dimen.width); // (width and height of ImageView)
Bitmap drawnBitmap = drawCanvas(bitmap, dimensionPixelSize);
canvas.drawBitmap(drawnBitmap, 0, 0, null);
}
private Bitmap drawCanvas(Bitmap recycledBitmap, int width) {
final Bitmap bitmap = verifyRecycledBitmap(recycledBitmap, width);
final Bitmap output = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Rect rect = new Rect(0, 0, width, width);
final int offset = (int) (width / (double) 2 * Math.tan(30 * Math.PI / (double) 180)); // (width / 2) * tan(30deg)
final int length = width - (2 * offset);
final Path path = new Path();
path.moveTo(width / 2, 0); // top
path.lineTo(0, offset); // left top
path.lineTo(0, offset + length); // left bottom
path.lineTo(width / 2, width); // bottom
path.lineTo(width, offset + length); // right bottom
path.lineTo(width, offset); // right top
path.close(); //back to top
Paint paint = new Paint();
paint.setStrokeWidth(4);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint); // draws the bitmap for the image
paint.setColor(Color.parseColor("white"));
paint.setStrokeWidth(4);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setPathEffect(new CornerPathEffect(10));
paint.setAntiAlias(true); // draws the border
canvas.drawPath(path, paint);
return output;
}
I was looking at some iOS code and they are able to apply an actual image as a mask to achieve this result. Is there anyway on Android to do something like that?
I was looking for the best approach for a long time. Your solution is pretty heavy and doesn't work well with animations. The clipPath approach doesn't use antialiasing and doesn't work with hardware acceleration on certain versions of Android (4.0 and 4.1?). Seems like the best approach (animation friendly, antialiased, pretty clean and hardware accelerated) is to use Canvas layers:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private static PorterDuffXfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
#Override
public void draw(Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(),
null, Canvas.ALL_SAVE_FLAG);
super.draw(canvas);
paint.setXfermode(pdMode);
canvas.drawBitmap(maskBitmap, 0, 0, paint);
canvas.restoreToCount(saveCount);
paint.setXfermode(null);
}
You can use any kind of mask including custom shapes and bitmaps. Carbon uses such approach to round corners of widgets on the fly.
Even though it might work, there a few bad mistakes on this implementation:
You're allocation some very big objects during onDraw phase and that leads to a terrible performance. The most important there is the createBitmap but you should avoid at all costs any new during onDraw. Pre-allocate all necessary objects during initialisation and re-use them during onDraw.
You should setup your path just once during onSizeChanged. Avoid all that path on every OnDraw
You're relying on usage of BitmapDrawable if for example you use Picasso to load images from the internet or if you want to use a selector, then this code won't work.
You should not need to allocate the second bitmap, use canvas.clipPath instead to make it efficient.
Said all that a much more efficient pseudo code for the drawing should be:
#Override
protected void onDraw(Canvas canvas) {
canvas.save(CLIP_SAVE_FLAG); // save the clipping
canvas.clipPath(path, Region.Op./*have to test which one*/ ); // cut the canvas
super.onDraw(canvas); // do the normal drawing
canvas.restore(); // restore the saved clipping
canvas.drawPath(path, paint); // draw the extra border
}
add dependency in build.gradle
implementation 'com.github.siyamed:android-shape-imageview:0.9.+#aar'
implementation group: 'net.sf.kxml', name: 'kxml2-min', version: '2.3.0'
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".MainActivity">
<com.github.siyamed.shapeimageview.HexagonImageView
android:id="#+id/imageView"
android:layout_marginTop="100dp"
android:layout_width="250dp"
android:layout_height="350dp" />
</LinearLayout>
activity
public class MainActivity extends AppCompatActivity {
HexagonImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView=findViewById(R.id.imageView);
imageView.setImageResource(R.drawable.img);
}}
output
add dependency in build.gradle
implementation 'com.github.siyamed:android-shape-imageview:0.9.+#aar'
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".MainActivity">
<com.github.siyamed.shapeimageview.mask.PorterShapeImageView
android:id="#+id/imageView"
android:layout_width="250dp"
android:layout_height="350dp"
app:siShape="#drawable/shape" />
</LinearLayout>
activity
public class MainActivity extends AppCompatActivity {
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView=findViewById(R.id.imageView);
imageView.setImageResource(R.drawable.img);
}
}
drawable resource
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFC107" />
<padding android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
<corners
android:topLeftRadius="0dip"
android:topRightRadius="70dip"
android:bottomLeftRadius="70dip"
android:bottomRightRadius="0dip" />
</shape>
output