How to set drawable in Top-Left in a textview? - android

<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="#dimen/padding_small_6"
android:includeFontPadding="false"
android:text="#string/forgot_password_hint"
android:drawableStart="#drawable/ic_edit"
android:textSize="#dimen/text_size_medium_14"/>
This is my textview. I want to set the drawable on left of this textview. But the problem is, If the text is multiline, then drawable appears center-vertical in the text view.
Is there any option to set drawable on Top-Left of the textview?

No, their is no any direct option to set drawable on Top-Left of the textview.
But you can create some custom design to achieve this. Replace your TextView with the below code
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="#drawable/ic_edit" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:text="#string/forgot_password_hint"
android:textSize="#dimen/text_size_medium_14"/>
</LinearLayout>

Similar to #Avinash Kumar Singh solution. If you have a small image it will be higher than a first line of text.
In this case you should center the image by the first line.
<?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:tool="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:drawableStartCompat="#drawable/dot"
tool:drawableStart="#drawable/dot" />
<TextView
android:id="#+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
tool:text="This is my textview. I want to set the drawable on left of this textview" />
</LinearLayout>

Make Custom drawable class by extending a drawable class and use for drawable .eg:
public class TopLeftGravityDrawable extends BitmapDrawable {
private final Drawable mDrawable;
public TopLeftGravityDrawable(Drawable drawable) {
mDrawable = drawable;
}
#Override
public int getIntrinsicWidth() {
return mDrawable.getIntrinsicWidth();
}
#Override
public int getIntrinsicHeight() {
return mDrawable.getIntrinsicHeight();
}
#Override
public void draw(Canvas canvas) {
int halfCanvas= canvas.getHeight() / 2;
int halfDrawable = mDrawable.getIntrinsicHeight() / 2;
// align to top
canvas.save();
canvas.translate(0, -halfCanvas + halfDrawable);
mDrawable.draw(canvas);
canvas.restore();
}
}
Usage :
Drawable drawable = getResources().getDrawable(R.drawable.ic_customer);
TopGravityDrawable gravityDrawable = new TopGravityDrawable(drawable);
drawable.setBounds(0, 6, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
gravityDrawable.setBounds(0, 6, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
textview.setCompoundDrawables(gravityDrawable, null, null, null);

If a programmatic approach is suitable, You could extend Drawable in such way that it occupies the full height available and draws on top.
If You want an solution using only interface builder, You should just wrap the TextView in a horizontal LinearLayout, add an ImageView and set ImageViews gravity to be TOP.

This has solved my issue:
class MyTextView #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, style) {
private val leftDrawable = ContextCompat.getDrawable(context, R.drawable.checkmark)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
setBulletPoint(compoundDrawables[0], canvas)
}
private fun setBulletPoint(drawableLeft: Drawable?, canvas: Canvas?) {
if (!TextUtils.isEmpty(text)) {
leftDrawable?.let { drlft ->
if (lineCount == 1) {
setCompoundDrawablesWithIntrinsicBounds(drlft, null, null, null)
} else {
val buttonWidth = drlft.intrinsicWidth
val buttonHeight = drlft.intrinsicHeight
val topSpace = abs(buttonHeight - lineHeight) / 2
drlft.setBounds(0, topSpace, buttonWidth, topSpace + buttonHeight)
canvas?.apply {
save()
drlft.draw(canvas)
restore()
}
}
}
}
}
}

Try this in xml file
change android:drawableStart to android:drawableLeft
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="#dimen/padding_small_6"
android:includeFontPadding="false"
android:text="#string/forgot_password_hint"
android:drawableLeft="#drawable/ic_edit"
android:textSize="#dimen/text_size_medium_14"/>

use android:paddingBottom="10dp"

Related

Button Layout with Profile Picture Alignment

I want to create buttons that look like in the picture. Inside the circle (which is transparent in the png) I want to place the profile picture of players. There should also be text on the blue bar.
I've got it working but it just seems way too complicated. I think it is easier to understand what I have done without giving code but if you need it I can add it. Here is the layout:
RelativeLayout
LinearLayout (horizontal orientation)
Empty view with weight 0.7
Profile Picture with weight 0.2
Empty view with weight 0.1
the overlay picture that I posted below
LinearLayout (horizontal orientation)
RelativeLayout with weight 0.7 (space where all the text can go)
empty view with weigh 0.3
By the way: to the right of the circle, the png isn't transparent but white!
This works well but there must be a better way! All these empty views just to align the picture to the right position is kind of ugly. And the fact that the overlay picture must go inbetween the profile picture and the text makes it even uglier.
I'd prefer to do it without a png as overlay but with simple shapes (so that it looks good on every screen) but I wouldn't know how to do that. Would you recommend that? And if yes, how could that be done?
Or do you have an idea how to improve the xml layout or how to do it otherwise.
Thanks very much
You can do it without any image:
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:weightSum="1.0">
<TextView
android:layout_weight="0.7"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="New Text"
android:id="#+id/textView"
android:background="#0073ff"/>
<ImageView
android:layout_weight="0.2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#drawable/half_round_drawable"
android:src="#drawable/profile"/>
</LinearLayout>
</LinearLayout>
half_round_drawable:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="oval">
<corners android:radius="16dp" />
<solid android:color="#0073ff" />
</shape>
</item>
<item
android:bottom="0dp"
android:right="32dp"> <!-- radius *2 -->
<shape>
<solid android:color="#0073ff" />
</shape>
</item>
</layer-list>
To make the profile-image round you should use something like this:
How to create a circular ImageView in Android?
You can use a simple LinearLayout if you confine the background image to the profile area at the right side. You can define the content area in the image itself if you use a nine-patch drawable, as follows:
Extract the profile portion from your background image file.
Create a nine patch drawable from it, defining all the area as stretchable (left and top border lines), and the empty circle as the content area (right and bottom lines).
Since you should ideally have the image at the foreground layer to ensure that the photo isn't drawn outside of the circle, you can use a FrameLayout with a foreground drawable to contain your profile photo's ImageView. There would also need to be another dummy child view to work around a bug in FrameLayout that causes a single child with match_parent dimensions to be layout incorrectly.
This is how the layout should look like at the end:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#00f" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foreground="#drawable/profile_bg">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/photo"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
Now I am ready to present my answer.
Portret:
Landscape:
Layout.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:shape="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<!--This is the CustomView which include -->
<!--own attributes: -->
<!--circle_radius is the radius of image, -->
<!--content_padding is the padding,-->
<!--and background_color is the color of shape.-->
<CustomShape
android:layout_width="match_parent"
android:layout_height="wrap_content"
shape:circle_radius="40dp"
shape:content_padding="8dp"
shape:background_color="#FF983493">
<!--There must be two Views:-->
<!--TextView and ImageView and only in this order.-->
<!--Set-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--to bot of them, because in CustomShape it will be-->
<!--resized for you. There also don`t need to set -->
<!--any kind of margin or location attributes.-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/txt"
android:padding="5dp"
android:textColor="#android:color/white"
android:text="sdsfkjsdkfhsdk flsdkfjkls asdfasd fklasdjl fkjasdklfjasd k "
android:background="#android:color/transparent"/>
<!--For RoundImage I use custom class which round the drawable,-->
<!--not a View. Look down.-->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/img"
android:src="#drawable/img"
android:scaleType="fitCenter"
android:background="#android:color/transparent" />
</CustomShape>
</RelativeLayout>
CustomShape class:
public class CustomShape extends RelativeLayout {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
int circleRadius; // image radius
int diameter; // image diameter
int contentPadding;
int semiPadding;
int rectRightSide;
int backgroundColor;
int viewWidth; // width of parent(CustomShape layout)
public CustomShape(Context context) {
super(context);
this.setWillNotDraw(false);
}
public CustomShape(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomShape, 0, 0);
try {
this.circleRadius = (int) ta.getDimension(R.styleable.CustomShape_circle_radius, 40);
this.contentPadding = (int) ta.getDimension(R.styleable.CustomShape_content_padding, 8);
this.backgroundColor = ta.getColor(R.styleable.CustomShape_background_color, 0);
this.semiPadding = contentPadding / 2;
this.diameter = circleRadius * 2;
} finally {
ta.recycle();
}
this.setWillNotDraw(false);
}
public CustomShape(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setWillNotDraw(false);
}
#Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
super.onSizeChanged(xNew, yNew, xOld, yOld);
viewWidth = xNew;
this.rectRightSide = viewWidth - circleRadius - (circleRadius / 2); // get position for image
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
ImageView img = (ImageView) this.getChildAt(1);
RelativeLayout.LayoutParams imgParams = new LayoutParams(diameter - contentPadding, diameter - contentPadding);
imgParams.leftMargin = rectRightSide - circleRadius + semiPadding;
imgParams.topMargin = semiPadding;
img.setLayoutParams(imgParams);
//Create custom RoundImage and set to image
try {
Drawable drawable = img.getDrawable();
Bitmap bm = ((BitmapDrawable) drawable).getBitmap();
RoundImage resultImage = new RoundImage(bm);
img.setImageDrawable(resultImage);
} catch (ClassCastException e) {
}
//Positioning and resizing TextView
View txt = this.getChildAt(0);
RelativeLayout.LayoutParams txtParams = new LayoutParams(rectRightSide - circleRadius - semiPadding, diameter - contentPadding);
txtParams.topMargin = semiPadding;
txtParams.leftMargin = semiPadding;
txt.setLayoutParams(txtParams);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
this.setMeasuredDimension(parentWidth, diameter); // set correct height
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(backgroundColor);
canvas.drawRect(0, 0, rectRightSide, diameter, paint);
//Draw circle
paint.setDither(true);
canvas.drawCircle(rectRightSide, circleRadius, circleRadius, paint);
}
}
Attr.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomShape">
<attr name="circle_radius" format="dimension" />
<attr name="content_padding" format="dimension" />
<attr name="background_color" format="color" />
</declare-styleable>
</resources>
RoundImage class:
public class RoundImage extends Drawable {
private final Bitmap mBitmap;
private final Paint mPaint;
private final RectF mRectF;
private final int mBitmapWidth;
private final int mBitmapHeight;
public RoundImage(Bitmap bitmap) {
mBitmap = bitmap;
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
}
#Override
public void draw(Canvas canvas) {
canvas.drawOval(mRectF, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRectF.set(bounds);
}
#Override
public void setAlpha(int alpha) {
if (mPaint.getAlpha() != alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
#Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
invalidateSelf();
}
#Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
#Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
public Bitmap getBitmap() {
return mBitmap;
}
}
Hope it will help you.

How to fit image in circular border and rectangular border [duplicate]

I'd Like to make any image from my ImageView to be circular with a border.
I searched but couldn't find any useful information (anything that I tried didn't work).
How can I achieve this through XML:
Create an ImageView with certain src and make it circular with a border?
This is the simplest way that I designed. Try this.
dependencies
implementation 'androidx.appcompat:appcompat:1.3.0-beta01'
implementation 'androidx.cardview:cardview:1.0.0'
<android.support.v7.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
android:elevation="12dp"
android:id="#+id/view2"
app:cardCornerRadius="40dp"
android:layout_centerHorizontal="true"
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="1.9">
<ImageView
android:layout_height="80dp"
android:layout_width="match_parent"
android:id="#+id/imageView1"
android:src="#drawable/YOUR_IMAGE"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true">
</ImageView>
</android.support.v7.widget.CardView>
If you are working on android versions above lollipop
<android.support.v7.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
android:elevation="12dp"
android:id="#+id/view2"
app:cardCornerRadius="40dp"
android:layout_centerHorizontal="true">
<ImageView
android:layout_height="80dp"
android:layout_width="match_parent"
android:id="#+id/imageView1"
android:src="#drawable/YOUR_IMAGE"
android:scaleType="centerCrop"/>
</android.support.v7.widget.CardView>
Adding Border to round ImageView - LATEST VERSION
Wrap it with another CardView slightly bigger than the inner one and set its background color to add a border to your round image. You can increase the size of the outer CardView to increase the thickness of the border.
<androidx.cardview.widget.CardView
android:layout_width="155dp"
android:layout_height="155dp"
app:cardCornerRadius="250dp"
app:cardBackgroundColor="#color/white">
<androidx.cardview.widget.CardView
android:layout_width="150dp"
android:layout_height="150dp"
app:cardCornerRadius="250dp"
android:layout_gravity="center">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/default_user"
android:scaleType="centerCrop"/>
</androidx.cardview.widget.CardView>
</androidx.cardview.widget.CardView>
You can make a simple circle with white border and transparent content with shape.
// res/drawable/circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="1.9"
android:useLevel="false" >
<solid android:color="#android:color/transparent" />
<stroke
android:width="10dp"
android:color="#android:color/white" />
</shape>
Then make a layerlist drawable and put it as background to your imageview.
// res/drawable/img.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/circle"/>
<item android:drawable="#drawable/ic_launcher"/>
</layer-list>
and put it as background to your imageview.
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/img"/>
You'll have something like that.
With the Material Components Library Just use the ShapeableImageView.
Somethig like:
<com.google.android.material.imageview.ShapeableImageView
app:shapeAppearanceOverlay="#style/roundedImageViewRounded"
app:strokeColor="#color/....."
app:strokeWidth="1dp"
...
/>
with:
<style name="roundedImageViewRounded">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">50%</item>
</style>
Note: it requires at least the version 1.2.0.
With jetpack compose you can apply the clip Modifier using a CircleShape:
Image(
painter = painterResource(R.drawable.xxxx),
contentDescription = "xxxx",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(100.dp)
.clip(CircleShape)
.border(2.dp, Color.Blue, CircleShape)
)
I hope this will help you.
1) ShapeableImageView
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:padding="5dp"
app:strokeWidth="10dp"
app:strokeColor="#android:color/darker_gray"
app:shapeAppearanceOverlay="#style/circleImageView"
android:src="#drawable/profile"
android:layout_margin="10dp"/>
Style add here: res/values/styles.xml
<style name="circleImageView" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">50%</item>
</style>
For complete description please check here : The Source here.
2) CircleImageView
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/profile_image"
android:layout_width="96dp"
android:layout_height="96dp"
android:src="#drawable/profile"
app:civ_border_width="2dp"
app:civ_border_color="#FF000000"/>
Don't forget implementation: Gradle Scripts > build.gradle (Module: app) > dependencies
implementation 'de.hdodenhof:circleimageview:3.1.0'
For complete description please check here : The Source here.
3) CircularImageView
<com.mikhaellopez.circularimageview.CircularImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:src="#drawable/image"
app:civ_border_color="#3f51b5"
app:civ_border_width="4dp"
app:civ_shadow="true"
app:civ_shadow_radius="10"
app:civ_shadow_color="#3f51b5"/>
Don't forget implementation: Gradle Scripts > build.gradle (Module: app) > dependencies
implementation 'com.mikhaellopez:circularimageview:4.3.1'
For complete description please check here : The Source here.
With the help of glide library and RoundedBitmapDrawableFactory class it's easy to achieve. You may need to create circular placeholder image.
Glide V4:
Glide.with(context).load(url).apply(RequestOptions.circleCropTransform()).into(imageView);
Glide V3:
Glide.with(context)
.load(imgUrl)
.asBitmap()
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.into(new BitmapImageViewTarget(imgProfilePicture) {
#Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(context.getResources(),
Bitmap.createScaledBitmap(resource, 50, 50, false));
drawable.setCircular(true);
imgProfilePicture.setImageDrawable(drawable);
}
});
For Picasso RoundedTransformation, this is a really great solution that gives an additional option of rounding image at either top or bottom edge.
The above methods don't seem to work if you're using the src attribute. What I did is to put two image views inside a frame layout one above another like this:
<FrameLayout android:id="#+id/frame"
android:layout_width="40dp"
android:layout_height="40dp">
<ImageView android:id="#+id/pic"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/my_picture" />
<ImageView android:id="#+id/circle_crop"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/circle_crop" />
</FrameLayout>
Simply put a circular_crop.png in your drawable folder which is in the shape of your image dimensions (a square in my case) with a white background and a transparent circle in the center. You can use this image if you have want a square imageview.
Just download the picture above.
The following is one of the simplest ways to do it, use the following code:
Dependencies
dependencies {
...
compile 'de.hdodenhof:circleimageview:2.1.0' // use this or use the latest compile version. In case u get bug.
}
XML Code
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/profile_image"
android:layout_width="96dp" // here u can adjust the width
android:layout_height="96dp" // here u can adjust the height
android:src="#drawable/profile" // here u can change the image
app:civ_border_width="2dp" // here u can adjust the border of the circle.
app:civ_border_color="#FF000000"/> // here u can adjust the border color
Screenshot:
Source: Circular ImageView GitHub Repository
you don't need any third-party library.
you can use the ShapeableImageView in the material.
implementation 'com.google.android.material:material:1.2.0'
style.xml
<style name="ShapeAppearanceOverlay.App.CornerSize">
<item name="cornerSize">50%</item>
</style>
in layout
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="#drawable/ic_profile"
app:shapeAppearanceOverlay="#style/ShapeAppearanceOverlay.App.CornerSize"
/>
you can see this
https://developer.android.com/reference/com/google/android/material/imageview/ShapeableImageView
or this
https://medium.com/android-beginners/shapeableimageview-material-components-for-android-cac6edac2c0d
You can simply use AndroidX ImageFilterView.
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="#dimen/margin_medium"
android:layout_marginBottom="#dimen/margin_medium"
android:background="#color/white"
android:padding="#dimen/margin_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:roundPercent="1"
app:srcCompat="#drawable/ic_gallery" />
You can simply use CardView without any external Library
<androidx.cardview.widget.CardView
android:id="#+id/roundCardView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerHorizontal="true"
android:elevation="0dp"
app:cardCornerRadius="20dp">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/profile" />
</androidx.cardview.widget.CardView>
This will do the trick:
rectangle.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/transparent" />
<padding android:bottom="-14dp" android:left="-14dp" android:right="-14dp" android:top="-14dp" />
</shape>
circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="oval"
android:useLevel="false" >
<solid android:color="#android:color/transparent" />
<stroke
android:width="15dp"
android:color="#color/verification_contact_background" />
</shape>
profile_image.xml ( The layerlist )
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/rectangle" />
<item android:drawable="#drawable/circle"/>
</layer-list>
Your layout
<ImageView
android:id="#+id/profile_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/default_org"
android:src="#drawable/profile_image"/>
Posting this answer for future reference. You can use ShapeableImageView available in com.google.android.material:material.
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/img_launcher_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:adjustViewBounds="true"
android:background="#android:color/transparent"
android:elevation="5dp"
android:maxWidth="50dp"
android:maxHeight="50dp"
android:scaleType="fitXY"
android:src="#mipmap/ic_launcher"
app:shapeAppearance="?attr/actionButtonStyle"
app:shapeAppearanceOverlay="#style/imageViewRounded"
app:strokeColor="#android:color/white" />
Add imageViewRounded style in your styles.xml
<style name="imageViewRounded">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">51%</item>
</style>
You can add material design dependency if not added.
implementation 'com.google.android.material:material:1.4.0'
Design looks like
Best Solution courtesy https://www.youtube.com/watch?v=0MHoNU7ytaw
the width and height of the card view determine the size of the images it contains set up is as follows:
Add Dependency to Gradle(Module)
Add the xml code to activity.xml or fragment.xml file
implementation 'androidx.cardview:cardview:1.0.0'
<androidx.cardview.widget.CardView
android:layout_width="300dp"
android:layout_height="270dp"
android:layout_gravity="center"
app:cardCornerRadius="150dp"
app:cardBackgroundColor="#color/trans"
>
<ImageView
android:id="#+id/resultImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/congrats"
android:layout_gravity="center">
</ImageView>
</androidx.cardview.widget.CardView>```
Update 2021: Using Glide v4 CircleCrop see https://bumptech.github.io/glide/doc/generatedapi.html
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
XML
<ImageView
android:id="#+id/vinyl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
In Code
Glide.with(this)
.load("https://images.pexels.com/photos/3828241/pexels-photo-3828241.jpeg")
.transform(CircleCrop())
.into(rootView.findViewById<ImageView>(R.id.vinyl))
I use shape = "oval" instead of the "ring" below. It has worked for me. To keep the image within bounds, I use <padding> and set <adjustViewBounds> to true in my <ImageView>. I have tried with images of size between 50 x 50 px upto 200x200 px .
#Jyotman Singh, answer is very good (for solid backgrounds), so I would like to enhance it by sharing vector drawable that can be re-colored for your needs, also it is convenient since vector one-piece shape is well scalable.
This is the rectangle-circle shape (#drawable/shape_round_profile_pic):
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="284"
android:viewportHeight="284"
android:width="284dp"
android:height="284dp">
<path
android:pathData="M0 142L0 0l142 0 142 0 0 142 0 142 -142 0 -142 0zm165 137.34231c26.06742 -4.1212 52.67405 -17.543 72.66855 -36.65787 11.82805 -11.30768 20.55487 -22.85153 27.7633 -36.72531C290.23789 158.21592 285.62874 101.14121 253.48951 58.078079 217.58149 9.9651706 154.68849 -10.125717 98.348685 8.5190299 48.695824 24.95084 12.527764 67.047123 3.437787 118.98655 1.4806194 130.16966 1.511302 152.96723 3.4990422 164.5 12.168375 214.79902 47.646316 256.70775 96 273.76783c21.72002 7.66322 44.26673 9.48476 69 5.57448z"
android:fillColor="#ffffff" /> // you can change frame color
</vector>
Usage is the same:
<FrameLayout
android:layout_width="70dp"
android:layout_height="70dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/YOUR_PICTURE" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/shape_round_profile_pic"/>
</FrameLayout>
Just use these lines of code and you are done :
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:clickable="true"
app:civ_border_width="3dp"
app:civ_border_color="#FFFFFFFF"
android:id="#+id/profile"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_below="#+id/header_cover_image"
android:layout_centerHorizontal="true"
android:layout_marginTop="-130dp"
android:elevation="5dp"
android:padding="20dp"
android:scaleType="centerCrop"
android:src="#drawable/profilemain" />
Don't forget to import :
import de.hdodenhof.circleimageview.CircleImageView;
Add this library in build.gradle :
compile 'de.hdodenhof:circleimageview:2.1.0'
If you use Material Design in your app then use this
<com.google.android.material.card.MaterialCardView
android:layout_width="75dp"
android:layout_height="75dp"
app:cardCornerRadius="50dp"
app:strokeWidth="1dp"
app:strokeColor="#color/black">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/circular_image"
android:scaleType="fitCenter"
android:src="#drawable/your_img" />
</com.google.android.material.card.MaterialCardView>
I did it like that, I used my background color in my vector image
ic_bg_picture.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M100.6,95.5c0,-0.4 -0.1,-0.7 0,-1.1c-0.2,-0.7 -0.2,-1.4 -0.1,-2.1c0,-0.1 0,-0.2 0,-0.3c-0.1,-0.6 -0.1,-1.2 0,-1.8c-1,-1.3 -0.3,-2.9 -0.3,-4.3c-0.1,-28.7 -0.1,-57.3 -0.1,-86C68,-0.1 35.9,-0.1 3.8,-0.2C0.7,-0.2 0,0.5 0,3.6c0.1,32.1 0.1,64.2 0.1,96.2c31,0 62,-0.1 92.9,0.1c3.6,0 6.3,-0.2 7.5,-3.2C100.5,96.4 100.5,95.9 100.6,95.5zM46.3,95.2C26.4,94 2,74.4 3.8,46.8C5.1,27.2 24.4,2.7 52.6,4.6c20.2,1.4 43,21.3 41.5,45.1C96.1,72.4 73,96.8 46.3,95.2z"
android:fillColor="#6200EE"/>
</vector>
in my case I created a vector and changed the android:fillColor="#6200EE"
by the color of my background
<ImageView
android:id="#+id/iv_profile_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="#string/app_name"
app:srcCompat="#color/colorPrimaryDark" />
<ImageView
android:id="#+id/container_profile_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="#string/app_name"
app:srcCompat="#drawable/ic_bg_picture"/>
Another method without using any library is using ImageFilterView and setting round percentage to the view will make the circle round
app:roundPercent="1"
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="#+id/ivProfile"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/custom_button_1"
app:roundPercent="1"
android:scaleType="fitXY"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/etName"/>
Try this.
public class RoundedImageView extends android.support.v7.widget.AppCompatImageView {
private int borderWidth = 4;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private BitmapShader shader;
public RoundedImageView(Context context)
{
super(context);
setup();
}
public RoundedImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
setup();
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setup();
}
private void setup()
{
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
setBorderColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.WHITE);
}
public void setBorderWidth(int borderWidth)
{
this.borderWidth = borderWidth;
this.invalidate();
}
public void setBorderColor(int borderColor)
{
if (paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
private void loadBitmap()
{
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if (bitmapDrawable != null)
image = bitmapDrawable.getBitmap();
}
#SuppressLint("DrawAllocation")
#Override
public void onDraw(Canvas canvas)
{
loadBitmap();
if (image != null)
{
shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
int circleCenter = viewWidth / 2;
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter + borderWidth - 4.0f, paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter - 4.0f, paint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width - (borderWidth * 2);
viewHeight = height - (borderWidth * 2);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY)
{
result = specSize;
}
else
{
// Measure the text
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY)
{
result = specSize;
}
else
{
result = viewHeight;
}
return (result + 2);
}
}
and use this ImageView in layout like:
<com.app.Demo.RoundedImageView
android:id="#+id/iv_profileImage"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerHorizontal="true"
/>
This Class is Custom Circular Imageview with shadow, Stroke,saturation and using this Custom Circular ImageView you can make your image in Circular Shape with Radius. Guys for Circular Shadow ImageView No need Github this class is enough.
Adding CircularImageView to your layout
CircularImageView c=new CircularImageView(this,screen width,screen height,Bitmap myimage);
yourLayout.addView(c);**
public class CircularImageView extends android.support.v7.widget.AppCompatImageView
{
private final Context context;
private final int width, height;
private final Paint paint;
private final Paint paintBorder,imagePaint;
private final Bitmap bitmap2;
private final Paint paint3;
private Bitmap bitmap;
private BitmapShader shader;
private float radius = 4.0f;
float x = 0.0f;
float y = 8.0f;
private float stroke;
private float strokeWidth = 0.0f;
private Bitmap bitmap3;
private int corner_radius=50;
public CircularImageView(Context context, int width, int height, Bitmap bitmap) {
super(context);
this.context = context;
this.width = width;
this.height = height;
//here "bitmap" is the square shape(width* width) scaled bitmap ..
this.bitmap = bitmap;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint3=new Paint();
paint3.setStyle(Paint.Style.STROKE);
paint3.setColor(Color.WHITE);
paint3.setAntiAlias(true);
paintBorder = new Paint();
imagePaint= new Paint();
paintBorder.setColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
this.bitmap2 = Bitmap.createScaledBitmap(bitmap, (bitmap.getWidth() - 40), (bitmap.getHeight() - 40), true);
imagePaint.setAntiAlias(true);
invalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Shader b;
if (bitmap3 != null)
b = new BitmapShader(bitmap3, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
else
b = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
imagePaint.setShader(b);
canvas.drawBitmap(maskedBitmap(), 20, 20, null);
}
private Bitmap maskedBitmap()
{
Bitmap l1 = Bitmap.createBitmap(width,width, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(l1);
paintBorder.setShadowLayer(radius, x, y, Color.parseColor("#454645"));
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
final RectF rect = new RectF();
rect.set(20, 20, bitmap2.getWidth(), bitmap2.getHeight());
canvas.drawRoundRect(rect, corner_radius, corner_radius, paintBorder);
canvas.drawRoundRect(rect, corner_radius, corner_radius, imagePaint);
if (strokeWidth!=0.0f)
{
paint3.setStrokeWidth(strokeWidth);
canvas.drawRoundRect(rect, corner_radius, corner_radius, paint3);
}
paint.setXfermode(null);
return l1;
}
// use seekbar here, here you have to pass "0 -- 250" here corner radius will change
public void setCornerRadius(int corner_radius)
{
this.corner_radius = corner_radius;
invalidate();
}
-------->use seekbar here, here you have to pass "0 -- 10.0f" here shadow radius will change
public void setShadow(float radius)
{
this.radius = radius;
invalidate();
}
// use seekbar here, here you have to pass "0 -- 10.0f" here stroke size will change
public void setStroke(float stroke)
{
this.strokeWidth = stroke;
invalidate();
}
private Bitmap updateSat(Bitmap src, float settingSat)
{
int w = src.getWidth();
int h = src.getHeight();
Bitmap bitmapResult =
Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvasResult = new Canvas(bitmapResult);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(settingSat);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvasResult.drawBitmap(src, 0, 0, paint);
return bitmapResult;
}
// use seekbar here, here you have to pass "0 -- 2.0f" here saturation will change
public void setSaturation(float sat)
{
System.out.println("qqqqqqqqqq "+sat);
bitmap3=updateSat(bitmap2, sat);
invalidate();
}
}
// Seekbar to change radius
radius_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
text_radius.setText(""+progress);
circularImageView.setCornerRadius(progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change shadow
shadow_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
float f= 4+progress/10.0f;
text_shadow.setText(""+progress);
circularImageView.setShadow(f);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change saturation
saturation_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
int progressSat = saturation_seekbar.getProgress();
float sat = (float) ((progressSat*4 / 100.0f)-1.0f);
circularImageView.setSaturation(sat);
text_saturation.setText(""+progressSat);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change stroke
stroke_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if (progress==0)
{
float f=(progress*10.0f/100.0f);
circularImageView.setStroke(f);
}
else
{
float f=(progress*10.0f/100.0f);
circularImageView.setStroke(f);
}
text_stroke.setText(""+progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//radius seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="50"
android:max="250"
android:id="#+id/radius_seekbar"
android:layout_height="wrap_content" />
//saturation seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="50"
android:max="100"
android:id="#+id/saturation_seekbar"
android:layout_height="wrap_content" />
//shadow seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="0"
android:max="100"
android:id="#+id/shadow_seekbar"
android:layout_height="wrap_content" />
//stroke seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="0"
android:max="100"
android:id="#+id/stroke _seekbar"
android:layout_height="wrap_content" />
Actually, you can use what Google provides via the support library RoundedBitmapDrawableFactory class (here and here), instead of using a third party library :
Gradle:
implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val originalDrawable = ContextCompat.getDrawable(this, R.drawable.avatar_1)!!
val bitmap = convertDrawableToBitmap(originalDrawable)
val drawable = RoundedBitmapDrawableFactory.create(resources, bitmap)
drawable.setAntiAlias(true)
drawable.cornerRadius = Math.max(bitmap.width, bitmap.height) / 2.0f
avatarImageView.setImageDrawable(drawable)
}
companion object {
#JvmStatic
fun convertDrawableToBitmap(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable)
return drawable.bitmap
// We ask for the bounds if they have been set as they would be most
// correct, then we check we are > 0
val bounds = drawable.bounds
val width = if (!bounds.isEmpty) bounds.width() else drawable.intrinsicWidth
val height = if (!bounds.isEmpty) bounds.height() else drawable.intrinsicHeight
// Now we check we are > 0
val bitmap = Bitmap.createBitmap(if (width <= 0) 1 else width, if (height <= 0) 1 else height,
Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
}
}
res/layout/activity_main.xml
<FrameLayout
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" tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/avatarImageView" android:layout_width="100dp" android:layout_height="100dp"
android:layout_gravity="center"/>
</FrameLayout>
res/drawable/avatar_1.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="128dp" android:height="128dp"
android:viewportHeight="128.0" android:viewportWidth="128.0">
<path
android:fillColor="#FF8A80" android:pathData="M0 0h128v128h-128z"/>
<path
android:fillColor="#FFE0B2"
android:pathData="M36.3 94.8c6.4 7.3 16.2 12.1 27.3 12.4 10.7,-.3 20.3,-4.7 26.7,-11.6l.2.1c-17,-13.3,-12.9,-23.4,-8.5,-28.6 1.3,-1.2 2.8,-2.5 4.4,-3.9l13.1,-11c1.5,-1.2 2.6,-3 2.9,-5.1.6,-4.4,-2.5,-8.4,-6.9,-9.1,-1.5,-.2,-3 0,-4.3.6,-.3,-1.3,-.4,-2.7,-1.6,-3.5,-1.4,-.9,-2.8,-1.7,-4.2,-2.5,-7.1,-3.9,-14.9,-6.6,-23,-7.9,-5.4,-.9,-11,-1.2,-16.1.7,-3.3 1.2,-6.1 3.2,-8.7 5.6,-1.3 1.2,-2.5 2.4,-3.7 3.7l-1.8 1.9c-.3.3,-.5.6,-.8.8,-.1.1,-.2 0,-.4.2.1.2.1.5.1.6,-1,-.3,-2.1,-.4,-3.2,-.2,-4.4.6,-7.5 4.7,-6.9 9.1.3 2.1 1.3 3.8 2.8 5.1l11 9.3c1.8 1.5 3.3 3.8 4.6 5.7 1.5 2.3 2.8 4.9 3.5 7.6 1.7 6.8,-.8 13.4,-5.4 18.4,-.5.6,-1.1 1,-1.4 1.7,-.2.6,-.4 1.3,-.6 2,-.4 1.5,-.5 3.1,-.3 4.6.4 3.1 1.8 6.1 4.1 8.2 3.3 3 8 4 12.4 4.5 5.2.6 10.5.7 15.7.2 4.5,-.4 9.1,-1.2 13,-3.4 5.6,-3.1 9.6,-8.9 10.5,-15.2m-14.4,-49.8c.9 0 1.6.7 1.6 1.6 0 .9,-.7 1.6,-1.6 1.6,-.9 0,-1.6,-.7,-1.6,-1.6,-.1,-.9.7,-1.6 1.6,-1.6zm-25.7 0c.9 0 1.6.7 1.6 1.6 0 .9,-.7 1.6,-1.6 1.6,-.9 0,-1.6,-.7,-1.6,-1.6,-.1,-.9.7,-1.6 1.6,-1.6z"/>
<path
android:fillColor="#E0F7FA"
android:pathData="M105.3 106.1c-.9,-1.3,-1.3,-1.9,-1.3,-1.9l-.2,-.3c-.6,-.9,-1.2,-1.7,-1.9,-2.4,-3.2,-3.5,-7.3,-5.4,-11.4,-5.7 0 0 .1 0 .1.1l-.2,-.1c-6.4 6.9,-16 11.3,-26.7 11.6,-11.2,-.3,-21.1,-5.1,-27.5,-12.6,-.1.2,-.2.4,-.2.5,-3.1.9,-6 2.7,-8.4 5.4l-.2.2s-.5.6,-1.5 1.7c-.9 1.1,-2.2 2.6,-3.7 4.5,-3.1 3.9,-7.2 9.5,-11.7 16.6,-.9 1.4,-1.7 2.8,-2.6 4.3h109.6c-3.4,-7.1,-6.5,-12.8,-8.9,-16.9,-1.5,-2.2,-2.6,-3.8,-3.3,-5z"/>
<path
android:fillColor="#444" android:pathData="M76.3,47.5 m-2.0, 0 a 2.0,2.0 0 1,1 4.0,0 a2.0,2.0 0 1,1 -4.0,0"/>
<path
android:fillColor="#444" android:pathData="M50.7,47.6 m-2.0, 0 a 2.0,2.0 0 1,1 4.0,0 a2.0,2.0 0 1,1 -4.0,0"/>
<path
android:fillColor="#444"
android:pathData="M48.1 27.4c4.5 5.9 15.5 12.1 42.4 8.4,-2.2,-6.9,-6.8,-12.6,-12.6,-16.4 17.2 1.5 14.1,-9.4 14.1,-9.4,-1.4 5.5,-11.1 4.4,-11.1 4.4h-18.8c-1.7,-.1,-3.4 0,-5.2.3,-12.8 1.8,-22.6 11.1,-25.7 22.9 10.6,-1.9 15.3,-7.6 16.9,-10.2z"/>
</vector>
The result:
And, suppose you want to add a border on top of it, you can use this for example:
stroke_drawable.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<stroke
android:width="4dp" android:color="#android:color/black"/>
</shape>
And add android:foreground="#drawable/stroke_drawable" to the ImageView in the layout XML file, and you get this :
I'm not sure how to add shadow (that will work on older Android versions), though. Using FloatingActionButton (from the "com.google.android.material:material" dependency), I failed to make the bitmap fill the FAB itself. Using it instead could be even better if it worked.
EDIT: if you wish to add shadow of elevation (available from API 21), you can change a bit what I wrote:
Inside the layout XML file:
<androidx.appcompat.widget.AppCompatImageView android:padding="4dp"
android:id="#+id/avatarImageView" android:layout_width="100dp" android:layout_height="100dp" android:elevation="8dp"
android:layout_gravity="center" android:background="#drawable/stroke_drawable" tools:srcCompat="#drawable/avatar_1"/>
CircularShadowViewOutlineProvider.kt
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
class CircularShadowViewOutlineProvider : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
val size = Math.max(view.width, view.height)
outline.setRoundRect(0, 0, size, size, size / 2f)
}
}
In code:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
avatarImageView.outlineProvider = CircularShadowViewOutlineProvider()
Result:
I have a simple solution.
Create a new Image asset by right clicking your package name and selecting New->Image asset.
Enter name (any name) and path (location of image in your system).
Then click Next and Finish.
If you enter name of image as 'img', a round image with the name 'img_round' is created automatically in mipmap folder.
Then, do this :
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#mipmap/img_round"/>
Your preview may still show a rectangular image. But if you run the app on your device, it will be round.
Create a CustomImageview then simply override its onDraw() method follows:
#Override
protected void onDraw(Canvas canvas) {
float radius = this.getHeight()/2;
Path path = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}
In case you want the code for the custom widget as well:-
CircularImageView.java
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.annotation.Nullable;
public class CircularImageView extends ImageView {
private Drawable image;
public CircularImageView(Context context) {
super(context);
init(null, 0);
}
public CircularImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public CircularImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas) {
float radius = this.getHeight()/2;
Path path = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}
private void init(AttributeSet attrs, int defStyle) {
TypedArray a = Utils.CONTEXT.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularImageView, 0, 0);
try {
image = a.getDrawable(R.styleable.CircularImageView_src);
} finally {
a.recycle();
}
this.setImageDrawable(image);
}
}
Also, add the following code to your res/attrs.xml to create the required attribute:-
<declare-styleable name="CircularImageView">
<attr name="src" format="reference" />
</declare-styleable>
if you want to set edit icon on to circle imageview than put this below code.
<FrameLayout
android:layout_width="#dimen/_100sdp"
android:layout_height="#dimen/_100sdp"
android:layout_gravity="center"
android:layout_marginTop="10dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/profilePic"
android:layout_width="#dimen/_100sdp"
android:layout_height="#dimen/_100sdp"
android:layout_gravity="bottom|center_horizontal"
android:src="#drawable/ic_upload" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/iv_camera"
android:layout_width="#dimen/_30sdp"
android:layout_height="#dimen/_30sdp"
android:layout_gravity="top|right"
android:src="#drawable/edit"/>
</FrameLayout>
if you'd rather cut the image to display in circular, here you go
public static Bitmap getCircularBitmap(Bitmap bitmap) {
Bitmap output;
if (bitmap.getWidth() > bitmap.getHeight()) {
output = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
} else {
output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getWidth(), Bitmap.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());
float r = 0;
if (bitmap.getWidth() > bitmap.getHeight()) {
r = bitmap.getHeight() / 2;
} else {
r = bitmap.getWidth() / 2;
}
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(r, r, r, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
Another idea is to use clipToOutline property of an ImageView.
Here is an example layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Simple view to draw borders for an image,
borders will be rounded because of the oval-shaped background. -->
<View
android:id="#+id/v_border"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/shape_border"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- Image itself: fits the border view,
a margin serves as a border width;
the key point here - is a background shape which will clip the view to its forms. -->
<ImageView
android:id="#+id/iv_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="4dp"
android:background="#drawable/shape_oval"
android:src="#mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="#+id/v_border"
app:layout_constraintEnd_toEndOf="#+id/v_border"
app:layout_constraintStart_toStartOf="#+id/v_border"
app:layout_constraintTop_toTopOf="#+id/v_border" />
</androidx.constraintlayout.widget.ConstraintLayout>
And here are our shape_border drawable:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#FF00FF" />
</shape>
And shape_oval drawable:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" />
The only thing you should do in the code - is to enable clipToOutline property:
binding.ivImage.clipToOutline = true
And of course you can avoid even this line of the code with some BindingAdapter.
This is a relatively old question, but you can just make a circle border in the drawable folder (let's assume the xml file will be called circle_border)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#android:color/transparent" />
<!-- If you want a padding -->
<padding android:top="4dp" android:left="4dp" android:right="4dp" android:bottom="4dp" />
<!-- If you want the circle border to have a color -->
<strong android:width="1dp" android:color="#FFFFFF" />
</shape>
Then you can use it as the background of the ImageView
<ImageView
android:background="#drawable/circle_border"
<!-- other attributes here -->
/>
just use this simple code:
First add dependency :
implementation 'de.hdodenhof:circleimageview:2.2.0'
then add in xml layout the following code:-
<de.hdodenhof.circleimageview.CircleImageView xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/Imgshaligram"
android:layout_width="96dp"
android:layout_height="96dp"
android:src="#drawable/shaligram"
app:civ_border_color="#d1b1b1"
android:foregroundGravity="center"/>

ImageView in circular through XML

I'd Like to make any image from my ImageView to be circular with a border.
I searched but couldn't find any useful information (anything that I tried didn't work).
How can I achieve this through XML:
Create an ImageView with certain src and make it circular with a border?
This is the simplest way that I designed. Try this.
dependencies
implementation 'androidx.appcompat:appcompat:1.3.0-beta01'
implementation 'androidx.cardview:cardview:1.0.0'
<android.support.v7.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
android:elevation="12dp"
android:id="#+id/view2"
app:cardCornerRadius="40dp"
android:layout_centerHorizontal="true"
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="1.9">
<ImageView
android:layout_height="80dp"
android:layout_width="match_parent"
android:id="#+id/imageView1"
android:src="#drawable/YOUR_IMAGE"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true">
</ImageView>
</android.support.v7.widget.CardView>
If you are working on android versions above lollipop
<android.support.v7.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
android:elevation="12dp"
android:id="#+id/view2"
app:cardCornerRadius="40dp"
android:layout_centerHorizontal="true">
<ImageView
android:layout_height="80dp"
android:layout_width="match_parent"
android:id="#+id/imageView1"
android:src="#drawable/YOUR_IMAGE"
android:scaleType="centerCrop"/>
</android.support.v7.widget.CardView>
Adding Border to round ImageView - LATEST VERSION
Wrap it with another CardView slightly bigger than the inner one and set its background color to add a border to your round image. You can increase the size of the outer CardView to increase the thickness of the border.
<androidx.cardview.widget.CardView
android:layout_width="155dp"
android:layout_height="155dp"
app:cardCornerRadius="250dp"
app:cardBackgroundColor="#color/white">
<androidx.cardview.widget.CardView
android:layout_width="150dp"
android:layout_height="150dp"
app:cardCornerRadius="250dp"
android:layout_gravity="center">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/default_user"
android:scaleType="centerCrop"/>
</androidx.cardview.widget.CardView>
</androidx.cardview.widget.CardView>
You can make a simple circle with white border and transparent content with shape.
// res/drawable/circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="1.9"
android:useLevel="false" >
<solid android:color="#android:color/transparent" />
<stroke
android:width="10dp"
android:color="#android:color/white" />
</shape>
Then make a layerlist drawable and put it as background to your imageview.
// res/drawable/img.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/circle"/>
<item android:drawable="#drawable/ic_launcher"/>
</layer-list>
and put it as background to your imageview.
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/img"/>
You'll have something like that.
With the Material Components Library Just use the ShapeableImageView.
Somethig like:
<com.google.android.material.imageview.ShapeableImageView
app:shapeAppearanceOverlay="#style/roundedImageViewRounded"
app:strokeColor="#color/....."
app:strokeWidth="1dp"
...
/>
with:
<style name="roundedImageViewRounded">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">50%</item>
</style>
Note: it requires at least the version 1.2.0.
With jetpack compose you can apply the clip Modifier using a CircleShape:
Image(
painter = painterResource(R.drawable.xxxx),
contentDescription = "xxxx",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(100.dp)
.clip(CircleShape)
.border(2.dp, Color.Blue, CircleShape)
)
I hope this will help you.
1) ShapeableImageView
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:padding="5dp"
app:strokeWidth="10dp"
app:strokeColor="#android:color/darker_gray"
app:shapeAppearanceOverlay="#style/circleImageView"
android:src="#drawable/profile"
android:layout_margin="10dp"/>
Style add here: res/values/styles.xml
<style name="circleImageView" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">50%</item>
</style>
For complete description please check here : The Source here.
2) CircleImageView
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/profile_image"
android:layout_width="96dp"
android:layout_height="96dp"
android:src="#drawable/profile"
app:civ_border_width="2dp"
app:civ_border_color="#FF000000"/>
Don't forget implementation: Gradle Scripts > build.gradle (Module: app) > dependencies
implementation 'de.hdodenhof:circleimageview:3.1.0'
For complete description please check here : The Source here.
3) CircularImageView
<com.mikhaellopez.circularimageview.CircularImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:src="#drawable/image"
app:civ_border_color="#3f51b5"
app:civ_border_width="4dp"
app:civ_shadow="true"
app:civ_shadow_radius="10"
app:civ_shadow_color="#3f51b5"/>
Don't forget implementation: Gradle Scripts > build.gradle (Module: app) > dependencies
implementation 'com.mikhaellopez:circularimageview:4.3.1'
For complete description please check here : The Source here.
With the help of glide library and RoundedBitmapDrawableFactory class it's easy to achieve. You may need to create circular placeholder image.
Glide V4:
Glide.with(context).load(url).apply(RequestOptions.circleCropTransform()).into(imageView);
Glide V3:
Glide.with(context)
.load(imgUrl)
.asBitmap()
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.into(new BitmapImageViewTarget(imgProfilePicture) {
#Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(context.getResources(),
Bitmap.createScaledBitmap(resource, 50, 50, false));
drawable.setCircular(true);
imgProfilePicture.setImageDrawable(drawable);
}
});
For Picasso RoundedTransformation, this is a really great solution that gives an additional option of rounding image at either top or bottom edge.
The above methods don't seem to work if you're using the src attribute. What I did is to put two image views inside a frame layout one above another like this:
<FrameLayout android:id="#+id/frame"
android:layout_width="40dp"
android:layout_height="40dp">
<ImageView android:id="#+id/pic"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/my_picture" />
<ImageView android:id="#+id/circle_crop"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/circle_crop" />
</FrameLayout>
Simply put a circular_crop.png in your drawable folder which is in the shape of your image dimensions (a square in my case) with a white background and a transparent circle in the center. You can use this image if you have want a square imageview.
Just download the picture above.
The following is one of the simplest ways to do it, use the following code:
Dependencies
dependencies {
...
compile 'de.hdodenhof:circleimageview:2.1.0' // use this or use the latest compile version. In case u get bug.
}
XML Code
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/profile_image"
android:layout_width="96dp" // here u can adjust the width
android:layout_height="96dp" // here u can adjust the height
android:src="#drawable/profile" // here u can change the image
app:civ_border_width="2dp" // here u can adjust the border of the circle.
app:civ_border_color="#FF000000"/> // here u can adjust the border color
Screenshot:
Source: Circular ImageView GitHub Repository
you don't need any third-party library.
you can use the ShapeableImageView in the material.
implementation 'com.google.android.material:material:1.2.0'
style.xml
<style name="ShapeAppearanceOverlay.App.CornerSize">
<item name="cornerSize">50%</item>
</style>
in layout
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="#drawable/ic_profile"
app:shapeAppearanceOverlay="#style/ShapeAppearanceOverlay.App.CornerSize"
/>
you can see this
https://developer.android.com/reference/com/google/android/material/imageview/ShapeableImageView
or this
https://medium.com/android-beginners/shapeableimageview-material-components-for-android-cac6edac2c0d
You can simply use AndroidX ImageFilterView.
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="#dimen/margin_medium"
android:layout_marginBottom="#dimen/margin_medium"
android:background="#color/white"
android:padding="#dimen/margin_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:roundPercent="1"
app:srcCompat="#drawable/ic_gallery" />
You can simply use CardView without any external Library
<androidx.cardview.widget.CardView
android:id="#+id/roundCardView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerHorizontal="true"
android:elevation="0dp"
app:cardCornerRadius="20dp">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/profile" />
</androidx.cardview.widget.CardView>
This will do the trick:
rectangle.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/transparent" />
<padding android:bottom="-14dp" android:left="-14dp" android:right="-14dp" android:top="-14dp" />
</shape>
circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadius="0dp"
android:shape="oval"
android:useLevel="false" >
<solid android:color="#android:color/transparent" />
<stroke
android:width="15dp"
android:color="#color/verification_contact_background" />
</shape>
profile_image.xml ( The layerlist )
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/rectangle" />
<item android:drawable="#drawable/circle"/>
</layer-list>
Your layout
<ImageView
android:id="#+id/profile_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/default_org"
android:src="#drawable/profile_image"/>
Posting this answer for future reference. You can use ShapeableImageView available in com.google.android.material:material.
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/img_launcher_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:adjustViewBounds="true"
android:background="#android:color/transparent"
android:elevation="5dp"
android:maxWidth="50dp"
android:maxHeight="50dp"
android:scaleType="fitXY"
android:src="#mipmap/ic_launcher"
app:shapeAppearance="?attr/actionButtonStyle"
app:shapeAppearanceOverlay="#style/imageViewRounded"
app:strokeColor="#android:color/white" />
Add imageViewRounded style in your styles.xml
<style name="imageViewRounded">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">51%</item>
</style>
You can add material design dependency if not added.
implementation 'com.google.android.material:material:1.4.0'
Design looks like
Best Solution courtesy https://www.youtube.com/watch?v=0MHoNU7ytaw
the width and height of the card view determine the size of the images it contains set up is as follows:
Add Dependency to Gradle(Module)
Add the xml code to activity.xml or fragment.xml file
implementation 'androidx.cardview:cardview:1.0.0'
<androidx.cardview.widget.CardView
android:layout_width="300dp"
android:layout_height="270dp"
android:layout_gravity="center"
app:cardCornerRadius="150dp"
app:cardBackgroundColor="#color/trans"
>
<ImageView
android:id="#+id/resultImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/congrats"
android:layout_gravity="center">
</ImageView>
</androidx.cardview.widget.CardView>```
Update 2021: Using Glide v4 CircleCrop see https://bumptech.github.io/glide/doc/generatedapi.html
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
XML
<ImageView
android:id="#+id/vinyl"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
In Code
Glide.with(this)
.load("https://images.pexels.com/photos/3828241/pexels-photo-3828241.jpeg")
.transform(CircleCrop())
.into(rootView.findViewById<ImageView>(R.id.vinyl))
I use shape = "oval" instead of the "ring" below. It has worked for me. To keep the image within bounds, I use <padding> and set <adjustViewBounds> to true in my <ImageView>. I have tried with images of size between 50 x 50 px upto 200x200 px .
#Jyotman Singh, answer is very good (for solid backgrounds), so I would like to enhance it by sharing vector drawable that can be re-colored for your needs, also it is convenient since vector one-piece shape is well scalable.
This is the rectangle-circle shape (#drawable/shape_round_profile_pic):
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="284"
android:viewportHeight="284"
android:width="284dp"
android:height="284dp">
<path
android:pathData="M0 142L0 0l142 0 142 0 0 142 0 142 -142 0 -142 0zm165 137.34231c26.06742 -4.1212 52.67405 -17.543 72.66855 -36.65787 11.82805 -11.30768 20.55487 -22.85153 27.7633 -36.72531C290.23789 158.21592 285.62874 101.14121 253.48951 58.078079 217.58149 9.9651706 154.68849 -10.125717 98.348685 8.5190299 48.695824 24.95084 12.527764 67.047123 3.437787 118.98655 1.4806194 130.16966 1.511302 152.96723 3.4990422 164.5 12.168375 214.79902 47.646316 256.70775 96 273.76783c21.72002 7.66322 44.26673 9.48476 69 5.57448z"
android:fillColor="#ffffff" /> // you can change frame color
</vector>
Usage is the same:
<FrameLayout
android:layout_width="70dp"
android:layout_height="70dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/YOUR_PICTURE" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/shape_round_profile_pic"/>
</FrameLayout>
Just use these lines of code and you are done :
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:clickable="true"
app:civ_border_width="3dp"
app:civ_border_color="#FFFFFFFF"
android:id="#+id/profile"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_below="#+id/header_cover_image"
android:layout_centerHorizontal="true"
android:layout_marginTop="-130dp"
android:elevation="5dp"
android:padding="20dp"
android:scaleType="centerCrop"
android:src="#drawable/profilemain" />
Don't forget to import :
import de.hdodenhof.circleimageview.CircleImageView;
Add this library in build.gradle :
compile 'de.hdodenhof:circleimageview:2.1.0'
If you use Material Design in your app then use this
<com.google.android.material.card.MaterialCardView
android:layout_width="75dp"
android:layout_height="75dp"
app:cardCornerRadius="50dp"
app:strokeWidth="1dp"
app:strokeColor="#color/black">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/circular_image"
android:scaleType="fitCenter"
android:src="#drawable/your_img" />
</com.google.android.material.card.MaterialCardView>
I did it like that, I used my background color in my vector image
ic_bg_picture.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M100.6,95.5c0,-0.4 -0.1,-0.7 0,-1.1c-0.2,-0.7 -0.2,-1.4 -0.1,-2.1c0,-0.1 0,-0.2 0,-0.3c-0.1,-0.6 -0.1,-1.2 0,-1.8c-1,-1.3 -0.3,-2.9 -0.3,-4.3c-0.1,-28.7 -0.1,-57.3 -0.1,-86C68,-0.1 35.9,-0.1 3.8,-0.2C0.7,-0.2 0,0.5 0,3.6c0.1,32.1 0.1,64.2 0.1,96.2c31,0 62,-0.1 92.9,0.1c3.6,0 6.3,-0.2 7.5,-3.2C100.5,96.4 100.5,95.9 100.6,95.5zM46.3,95.2C26.4,94 2,74.4 3.8,46.8C5.1,27.2 24.4,2.7 52.6,4.6c20.2,1.4 43,21.3 41.5,45.1C96.1,72.4 73,96.8 46.3,95.2z"
android:fillColor="#6200EE"/>
</vector>
in my case I created a vector and changed the android:fillColor="#6200EE"
by the color of my background
<ImageView
android:id="#+id/iv_profile_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="#string/app_name"
app:srcCompat="#color/colorPrimaryDark" />
<ImageView
android:id="#+id/container_profile_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="#string/app_name"
app:srcCompat="#drawable/ic_bg_picture"/>
Another method without using any library is using ImageFilterView and setting round percentage to the view will make the circle round
app:roundPercent="1"
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="#+id/ivProfile"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/custom_button_1"
app:roundPercent="1"
android:scaleType="fitXY"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/etName"/>
Try this.
public class RoundedImageView extends android.support.v7.widget.AppCompatImageView {
private int borderWidth = 4;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private BitmapShader shader;
public RoundedImageView(Context context)
{
super(context);
setup();
}
public RoundedImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
setup();
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setup();
}
private void setup()
{
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
setBorderColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.WHITE);
}
public void setBorderWidth(int borderWidth)
{
this.borderWidth = borderWidth;
this.invalidate();
}
public void setBorderColor(int borderColor)
{
if (paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
private void loadBitmap()
{
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if (bitmapDrawable != null)
image = bitmapDrawable.getBitmap();
}
#SuppressLint("DrawAllocation")
#Override
public void onDraw(Canvas canvas)
{
loadBitmap();
if (image != null)
{
shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
int circleCenter = viewWidth / 2;
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter + borderWidth - 4.0f, paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter - 4.0f, paint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width - (borderWidth * 2);
viewHeight = height - (borderWidth * 2);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY)
{
result = specSize;
}
else
{
// Measure the text
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY)
{
result = specSize;
}
else
{
result = viewHeight;
}
return (result + 2);
}
}
and use this ImageView in layout like:
<com.app.Demo.RoundedImageView
android:id="#+id/iv_profileImage"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerHorizontal="true"
/>
This Class is Custom Circular Imageview with shadow, Stroke,saturation and using this Custom Circular ImageView you can make your image in Circular Shape with Radius. Guys for Circular Shadow ImageView No need Github this class is enough.
Adding CircularImageView to your layout
CircularImageView c=new CircularImageView(this,screen width,screen height,Bitmap myimage);
yourLayout.addView(c);**
public class CircularImageView extends android.support.v7.widget.AppCompatImageView
{
private final Context context;
private final int width, height;
private final Paint paint;
private final Paint paintBorder,imagePaint;
private final Bitmap bitmap2;
private final Paint paint3;
private Bitmap bitmap;
private BitmapShader shader;
private float radius = 4.0f;
float x = 0.0f;
float y = 8.0f;
private float stroke;
private float strokeWidth = 0.0f;
private Bitmap bitmap3;
private int corner_radius=50;
public CircularImageView(Context context, int width, int height, Bitmap bitmap) {
super(context);
this.context = context;
this.width = width;
this.height = height;
//here "bitmap" is the square shape(width* width) scaled bitmap ..
this.bitmap = bitmap;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint3=new Paint();
paint3.setStyle(Paint.Style.STROKE);
paint3.setColor(Color.WHITE);
paint3.setAntiAlias(true);
paintBorder = new Paint();
imagePaint= new Paint();
paintBorder.setColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
this.bitmap2 = Bitmap.createScaledBitmap(bitmap, (bitmap.getWidth() - 40), (bitmap.getHeight() - 40), true);
imagePaint.setAntiAlias(true);
invalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Shader b;
if (bitmap3 != null)
b = new BitmapShader(bitmap3, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
else
b = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
imagePaint.setShader(b);
canvas.drawBitmap(maskedBitmap(), 20, 20, null);
}
private Bitmap maskedBitmap()
{
Bitmap l1 = Bitmap.createBitmap(width,width, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(l1);
paintBorder.setShadowLayer(radius, x, y, Color.parseColor("#454645"));
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
final RectF rect = new RectF();
rect.set(20, 20, bitmap2.getWidth(), bitmap2.getHeight());
canvas.drawRoundRect(rect, corner_radius, corner_radius, paintBorder);
canvas.drawRoundRect(rect, corner_radius, corner_radius, imagePaint);
if (strokeWidth!=0.0f)
{
paint3.setStrokeWidth(strokeWidth);
canvas.drawRoundRect(rect, corner_radius, corner_radius, paint3);
}
paint.setXfermode(null);
return l1;
}
// use seekbar here, here you have to pass "0 -- 250" here corner radius will change
public void setCornerRadius(int corner_radius)
{
this.corner_radius = corner_radius;
invalidate();
}
-------->use seekbar here, here you have to pass "0 -- 10.0f" here shadow radius will change
public void setShadow(float radius)
{
this.radius = radius;
invalidate();
}
// use seekbar here, here you have to pass "0 -- 10.0f" here stroke size will change
public void setStroke(float stroke)
{
this.strokeWidth = stroke;
invalidate();
}
private Bitmap updateSat(Bitmap src, float settingSat)
{
int w = src.getWidth();
int h = src.getHeight();
Bitmap bitmapResult =
Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvasResult = new Canvas(bitmapResult);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(settingSat);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvasResult.drawBitmap(src, 0, 0, paint);
return bitmapResult;
}
// use seekbar here, here you have to pass "0 -- 2.0f" here saturation will change
public void setSaturation(float sat)
{
System.out.println("qqqqqqqqqq "+sat);
bitmap3=updateSat(bitmap2, sat);
invalidate();
}
}
// Seekbar to change radius
radius_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
text_radius.setText(""+progress);
circularImageView.setCornerRadius(progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change shadow
shadow_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
float f= 4+progress/10.0f;
text_shadow.setText(""+progress);
circularImageView.setShadow(f);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change saturation
saturation_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
int progressSat = saturation_seekbar.getProgress();
float sat = (float) ((progressSat*4 / 100.0f)-1.0f);
circularImageView.setSaturation(sat);
text_saturation.setText(""+progressSat);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Seekbar to change stroke
stroke_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if (progress==0)
{
float f=(progress*10.0f/100.0f);
circularImageView.setStroke(f);
}
else
{
float f=(progress*10.0f/100.0f);
circularImageView.setStroke(f);
}
text_stroke.setText(""+progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//radius seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="50"
android:max="250"
android:id="#+id/radius_seekbar"
android:layout_height="wrap_content" />
//saturation seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="50"
android:max="100"
android:id="#+id/saturation_seekbar"
android:layout_height="wrap_content" />
//shadow seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="0"
android:max="100"
android:id="#+id/shadow_seekbar"
android:layout_height="wrap_content" />
//stroke seekbar in xml file
<SeekBar
android:layout_width="match_parent"
android:layout_gravity="center"
android:progress="0"
android:max="100"
android:id="#+id/stroke _seekbar"
android:layout_height="wrap_content" />
Actually, you can use what Google provides via the support library RoundedBitmapDrawableFactory class (here and here), instead of using a third party library :
Gradle:
implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val originalDrawable = ContextCompat.getDrawable(this, R.drawable.avatar_1)!!
val bitmap = convertDrawableToBitmap(originalDrawable)
val drawable = RoundedBitmapDrawableFactory.create(resources, bitmap)
drawable.setAntiAlias(true)
drawable.cornerRadius = Math.max(bitmap.width, bitmap.height) / 2.0f
avatarImageView.setImageDrawable(drawable)
}
companion object {
#JvmStatic
fun convertDrawableToBitmap(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable)
return drawable.bitmap
// We ask for the bounds if they have been set as they would be most
// correct, then we check we are > 0
val bounds = drawable.bounds
val width = if (!bounds.isEmpty) bounds.width() else drawable.intrinsicWidth
val height = if (!bounds.isEmpty) bounds.height() else drawable.intrinsicHeight
// Now we check we are > 0
val bitmap = Bitmap.createBitmap(if (width <= 0) 1 else width, if (height <= 0) 1 else height,
Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
}
}
res/layout/activity_main.xml
<FrameLayout
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" tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/avatarImageView" android:layout_width="100dp" android:layout_height="100dp"
android:layout_gravity="center"/>
</FrameLayout>
res/drawable/avatar_1.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="128dp" android:height="128dp"
android:viewportHeight="128.0" android:viewportWidth="128.0">
<path
android:fillColor="#FF8A80" android:pathData="M0 0h128v128h-128z"/>
<path
android:fillColor="#FFE0B2"
android:pathData="M36.3 94.8c6.4 7.3 16.2 12.1 27.3 12.4 10.7,-.3 20.3,-4.7 26.7,-11.6l.2.1c-17,-13.3,-12.9,-23.4,-8.5,-28.6 1.3,-1.2 2.8,-2.5 4.4,-3.9l13.1,-11c1.5,-1.2 2.6,-3 2.9,-5.1.6,-4.4,-2.5,-8.4,-6.9,-9.1,-1.5,-.2,-3 0,-4.3.6,-.3,-1.3,-.4,-2.7,-1.6,-3.5,-1.4,-.9,-2.8,-1.7,-4.2,-2.5,-7.1,-3.9,-14.9,-6.6,-23,-7.9,-5.4,-.9,-11,-1.2,-16.1.7,-3.3 1.2,-6.1 3.2,-8.7 5.6,-1.3 1.2,-2.5 2.4,-3.7 3.7l-1.8 1.9c-.3.3,-.5.6,-.8.8,-.1.1,-.2 0,-.4.2.1.2.1.5.1.6,-1,-.3,-2.1,-.4,-3.2,-.2,-4.4.6,-7.5 4.7,-6.9 9.1.3 2.1 1.3 3.8 2.8 5.1l11 9.3c1.8 1.5 3.3 3.8 4.6 5.7 1.5 2.3 2.8 4.9 3.5 7.6 1.7 6.8,-.8 13.4,-5.4 18.4,-.5.6,-1.1 1,-1.4 1.7,-.2.6,-.4 1.3,-.6 2,-.4 1.5,-.5 3.1,-.3 4.6.4 3.1 1.8 6.1 4.1 8.2 3.3 3 8 4 12.4 4.5 5.2.6 10.5.7 15.7.2 4.5,-.4 9.1,-1.2 13,-3.4 5.6,-3.1 9.6,-8.9 10.5,-15.2m-14.4,-49.8c.9 0 1.6.7 1.6 1.6 0 .9,-.7 1.6,-1.6 1.6,-.9 0,-1.6,-.7,-1.6,-1.6,-.1,-.9.7,-1.6 1.6,-1.6zm-25.7 0c.9 0 1.6.7 1.6 1.6 0 .9,-.7 1.6,-1.6 1.6,-.9 0,-1.6,-.7,-1.6,-1.6,-.1,-.9.7,-1.6 1.6,-1.6z"/>
<path
android:fillColor="#E0F7FA"
android:pathData="M105.3 106.1c-.9,-1.3,-1.3,-1.9,-1.3,-1.9l-.2,-.3c-.6,-.9,-1.2,-1.7,-1.9,-2.4,-3.2,-3.5,-7.3,-5.4,-11.4,-5.7 0 0 .1 0 .1.1l-.2,-.1c-6.4 6.9,-16 11.3,-26.7 11.6,-11.2,-.3,-21.1,-5.1,-27.5,-12.6,-.1.2,-.2.4,-.2.5,-3.1.9,-6 2.7,-8.4 5.4l-.2.2s-.5.6,-1.5 1.7c-.9 1.1,-2.2 2.6,-3.7 4.5,-3.1 3.9,-7.2 9.5,-11.7 16.6,-.9 1.4,-1.7 2.8,-2.6 4.3h109.6c-3.4,-7.1,-6.5,-12.8,-8.9,-16.9,-1.5,-2.2,-2.6,-3.8,-3.3,-5z"/>
<path
android:fillColor="#444" android:pathData="M76.3,47.5 m-2.0, 0 a 2.0,2.0 0 1,1 4.0,0 a2.0,2.0 0 1,1 -4.0,0"/>
<path
android:fillColor="#444" android:pathData="M50.7,47.6 m-2.0, 0 a 2.0,2.0 0 1,1 4.0,0 a2.0,2.0 0 1,1 -4.0,0"/>
<path
android:fillColor="#444"
android:pathData="M48.1 27.4c4.5 5.9 15.5 12.1 42.4 8.4,-2.2,-6.9,-6.8,-12.6,-12.6,-16.4 17.2 1.5 14.1,-9.4 14.1,-9.4,-1.4 5.5,-11.1 4.4,-11.1 4.4h-18.8c-1.7,-.1,-3.4 0,-5.2.3,-12.8 1.8,-22.6 11.1,-25.7 22.9 10.6,-1.9 15.3,-7.6 16.9,-10.2z"/>
</vector>
The result:
And, suppose you want to add a border on top of it, you can use this for example:
stroke_drawable.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<stroke
android:width="4dp" android:color="#android:color/black"/>
</shape>
And add android:foreground="#drawable/stroke_drawable" to the ImageView in the layout XML file, and you get this :
I'm not sure how to add shadow (that will work on older Android versions), though. Using FloatingActionButton (from the "com.google.android.material:material" dependency), I failed to make the bitmap fill the FAB itself. Using it instead could be even better if it worked.
EDIT: if you wish to add shadow of elevation (available from API 21), you can change a bit what I wrote:
Inside the layout XML file:
<androidx.appcompat.widget.AppCompatImageView android:padding="4dp"
android:id="#+id/avatarImageView" android:layout_width="100dp" android:layout_height="100dp" android:elevation="8dp"
android:layout_gravity="center" android:background="#drawable/stroke_drawable" tools:srcCompat="#drawable/avatar_1"/>
CircularShadowViewOutlineProvider.kt
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
class CircularShadowViewOutlineProvider : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
val size = Math.max(view.width, view.height)
outline.setRoundRect(0, 0, size, size, size / 2f)
}
}
In code:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
avatarImageView.outlineProvider = CircularShadowViewOutlineProvider()
Result:
I have a simple solution.
Create a new Image asset by right clicking your package name and selecting New->Image asset.
Enter name (any name) and path (location of image in your system).
Then click Next and Finish.
If you enter name of image as 'img', a round image with the name 'img_round' is created automatically in mipmap folder.
Then, do this :
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#mipmap/img_round"/>
Your preview may still show a rectangular image. But if you run the app on your device, it will be round.
Create a CustomImageview then simply override its onDraw() method follows:
#Override
protected void onDraw(Canvas canvas) {
float radius = this.getHeight()/2;
Path path = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}
In case you want the code for the custom widget as well:-
CircularImageView.java
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.annotation.Nullable;
public class CircularImageView extends ImageView {
private Drawable image;
public CircularImageView(Context context) {
super(context);
init(null, 0);
}
public CircularImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public CircularImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
#Override
protected void onDraw(Canvas canvas) {
float radius = this.getHeight()/2;
Path path = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}
private void init(AttributeSet attrs, int defStyle) {
TypedArray a = Utils.CONTEXT.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularImageView, 0, 0);
try {
image = a.getDrawable(R.styleable.CircularImageView_src);
} finally {
a.recycle();
}
this.setImageDrawable(image);
}
}
Also, add the following code to your res/attrs.xml to create the required attribute:-
<declare-styleable name="CircularImageView">
<attr name="src" format="reference" />
</declare-styleable>
if you want to set edit icon on to circle imageview than put this below code.
<FrameLayout
android:layout_width="#dimen/_100sdp"
android:layout_height="#dimen/_100sdp"
android:layout_gravity="center"
android:layout_marginTop="10dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/profilePic"
android:layout_width="#dimen/_100sdp"
android:layout_height="#dimen/_100sdp"
android:layout_gravity="bottom|center_horizontal"
android:src="#drawable/ic_upload" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/iv_camera"
android:layout_width="#dimen/_30sdp"
android:layout_height="#dimen/_30sdp"
android:layout_gravity="top|right"
android:src="#drawable/edit"/>
</FrameLayout>
if you'd rather cut the image to display in circular, here you go
public static Bitmap getCircularBitmap(Bitmap bitmap) {
Bitmap output;
if (bitmap.getWidth() > bitmap.getHeight()) {
output = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
} else {
output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getWidth(), Bitmap.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());
float r = 0;
if (bitmap.getWidth() > bitmap.getHeight()) {
r = bitmap.getHeight() / 2;
} else {
r = bitmap.getWidth() / 2;
}
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(r, r, r, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
Another idea is to use clipToOutline property of an ImageView.
Here is an example layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Simple view to draw borders for an image,
borders will be rounded because of the oval-shaped background. -->
<View
android:id="#+id/v_border"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#drawable/shape_border"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- Image itself: fits the border view,
a margin serves as a border width;
the key point here - is a background shape which will clip the view to its forms. -->
<ImageView
android:id="#+id/iv_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="4dp"
android:background="#drawable/shape_oval"
android:src="#mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="#+id/v_border"
app:layout_constraintEnd_toEndOf="#+id/v_border"
app:layout_constraintStart_toStartOf="#+id/v_border"
app:layout_constraintTop_toTopOf="#+id/v_border" />
</androidx.constraintlayout.widget.ConstraintLayout>
And here are our shape_border drawable:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#FF00FF" />
</shape>
And shape_oval drawable:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" />
The only thing you should do in the code - is to enable clipToOutline property:
binding.ivImage.clipToOutline = true
And of course you can avoid even this line of the code with some BindingAdapter.
This is a relatively old question, but you can just make a circle border in the drawable folder (let's assume the xml file will be called circle_border)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#android:color/transparent" />
<!-- If you want a padding -->
<padding android:top="4dp" android:left="4dp" android:right="4dp" android:bottom="4dp" />
<!-- If you want the circle border to have a color -->
<strong android:width="1dp" android:color="#FFFFFF" />
</shape>
Then you can use it as the background of the ImageView
<ImageView
android:background="#drawable/circle_border"
<!-- other attributes here -->
/>
just use this simple code:
First add dependency :
implementation 'de.hdodenhof:circleimageview:2.2.0'
then add in xml layout the following code:-
<de.hdodenhof.circleimageview.CircleImageView xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/Imgshaligram"
android:layout_width="96dp"
android:layout_height="96dp"
android:src="#drawable/shaligram"
app:civ_border_color="#d1b1b1"
android:foregroundGravity="center"/>

Aligning drawableLeft with text of button

Here is my layout:
The issue I'm facing is with the drawable checkmark. How would I go about aligning it next to the text, both of them centered within the button? Here is the XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PostAssignmentActivity" >
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal" >
<Button
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableLeft="#drawable/ic_checkmark_holo_light"
android:text="Post" />
<Button
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancel" />
</LinearLayout>
</RelativeLayout>
Applying android:gravity="center_vertical" pulls the text and drawable together, but then the text is no longer aligned in the center.
Solution 1
Set android:paddingLeft inside your first button. This will force the drawableLeft by paddingLeft amount to the right. This is the fast/hacky solution.
Solution 2
Instead of using a ButtonView, use a LinearLayout that contains both a textview and imageview. This is a better solution. It gives you more flexibility in the positioning of the checkmark.
Replace your ButtonView with the following code. You need the LinearLayout and TextView to use buttonBarButtonStyle so that the background colors are correct on selection and the text size is correct. You need to set android:background="#0000" for the children, so that only the LinearLayout handles the background coloring.
<LinearLayout
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal" >
<ImageView
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:background="#0000"
android:src="#drawable/ic_checkmark_holo_light"/>
<TextView
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:background="#0000"
android:text="Done" />
</LinearLayout>
Here are some screenshots I took while trying this out.
None of these solutions worked correctly without presenting unacceptable trade-offs (create a layout with views in it? Not a good idea). So why not roll your own? This is what I got:
First create an attrs.xml with this:
<resources>
<declare-styleable name="IconButton">
<attr name="iconSrc" format="reference" />
<attr name="iconSize" format="dimension" />
<attr name="iconPadding" format="dimension" />
</declare-styleable>
</resources>
This allows to create an icon with specific size, padding from text, and image in our new view. The view code looks like this:
public class IconButton extends Button {
private Bitmap mIcon;
private Paint mPaint;
private Rect mSrcRect;
private int mIconPadding;
private int mIconSize;
public IconButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public IconButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public IconButton(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
int shift = (mIconSize + mIconPadding) / 2;
canvas.save();
canvas.translate(shift, 0);
super.onDraw(canvas);
if (mIcon != null) {
float textWidth = getPaint().measureText((String)getText());
int left = (int)((getWidth() / 2f) - (textWidth / 2f) - mIconSize - mIconPadding);
int top = getHeight()/2 - mIconSize/2;
Rect destRect = new Rect(left, top, left + mIconSize, top + mIconSize);
canvas.drawBitmap(mIcon, mSrcRect, destRect, mPaint);
}
canvas.restore();
}
private void init(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.IconButton);
for (int i = 0; i < array.getIndexCount(); ++i) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.IconButton_iconSrc:
mIcon = drawableToBitmap(array.getDrawable(attr));
break;
case R.styleable.IconButton_iconPadding:
mIconPadding = array.getDimensionPixelSize(attr, 0);
break;
case R.styleable.IconButton_iconSize:
mIconSize = array.getDimensionPixelSize(attr, 0);
break;
default:
break;
}
}
array.recycle();
//If we didn't supply an icon in the XML
if(mIcon != null){
mPaint = new Paint();
mSrcRect = new Rect(0, 0, mIcon.getWidth(), mIcon.getHeight());
}
}
public static Bitmap drawableToBitmap (Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable)drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
And then it can be used like this:
<com.example.grennis.myapplication.IconButton
android:layout_width="200dp"
android:layout_height="64dp"
android:text="Delete"
app:iconSrc="#android:drawable/ic_delete"
app:iconSize="32dp"
app:iconPadding="6dp" />
This works for me.
You can use
<com.google.android.material.button.MaterialButton/> .
https://material.io/develop/android/components/material-button/
It finally allows setting the icon gravity.
<com.google.android.material.button.MaterialButton
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:text="Awesome button"
app:icon="#drawable/your_icon"
app:iconGravity="textStart" />
Here is a clean easy way, without doing anything fancy, to achieve the results of having a Button that is much wider than the content with Image and Text which are centered.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:background="#drawable/button_background_selector">
<Button
android:layout_centerInParent="true"
android:gravity="center"
android:duplicateParentState="true"
android:layout_width="wrap_content"
android:text="New User"
android:textSize="15sp"
android:id="#android:id/button1"
android:textColor="#android:color/white"
android:drawablePadding="6dp"
android:drawableLeft="#drawable/add_round_border_32x32"
android:layout_height="64dp" />
</RelativeLayout>
In our case, we wanted to use the default Button class (to inherit its various styles and behaviors) and we needed to be able to create the button in code. Also, in our case we could have text, an icon (left drawable), or both.
The goal was to center the icon and/or text as a group when the button width was wider than wrap_content.
public class CenteredButton extends Button
{
public CenteredButton(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
// We always want our icon and/or text grouped and centered. We have to left align the text to
// the (possible) left drawable in order to then be able to center them in our onDraw() below.
//
setGravity(Gravity.LEFT|Gravity.CENTER_VERTICAL);
}
#Override
protected void onDraw(Canvas canvas)
{
// We want the icon and/or text grouped together and centered as a group.
// We need to accommodate any existing padding
//
float buttonContentWidth = getWidth() - getPaddingLeft() - getPaddingRight();
// In later versions of Android, an "all caps" transform is applied to buttons. We need to get
// the transformed text in order to measure it.
//
TransformationMethod method = getTransformationMethod();
String buttonText = ((method != null) ? method.getTransformation(getText(), this) : getText()).toString();
float textWidth = getPaint().measureText(buttonText);
// Compute left drawable width, if any
//
Drawable[] drawables = getCompoundDrawables();
Drawable drawableLeft = drawables[0];
int drawableWidth = (drawableLeft != null) ? drawableLeft.getIntrinsicWidth() : 0;
// We only count the drawable padding if there is both an icon and text
//
int drawablePadding = ((textWidth > 0) && (drawableLeft != null)) ? getCompoundDrawablePadding() : 0;
// Adjust contents to center
//
float bodyWidth = textWidth + drawableWidth + drawablePadding;
canvas.translate((buttonContentWidth - bodyWidth) / 2, 0);
super.onDraw(canvas);
}
}
Here is my code and working perfect.
<Button
android:id="#+id/button"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="#drawable/green_btn_selector"
android:gravity="left|center_vertical"
android:paddingLeft="50dp"
android:drawableLeft="#drawable/plus"
android:drawablePadding="5dp"
android:text="#string/create_iou"
android:textColor="#color/white" />
public class DrawableCenterTextView extends TextView {
public DrawableCenterTextView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public DrawableCenterTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawableCenterTextView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
Drawable[] drawables = getCompoundDrawables();
if (drawables != null) {
Drawable drawableLeft = drawables[0];
Drawable drawableRight = drawables[2];
if (drawableLeft != null || drawableRight != null) {
float textWidth = getPaint().measureText(getText().toString());
int drawablePadding = getCompoundDrawablePadding();
int drawableWidth = 0;
if (drawableLeft != null)
drawableWidth = drawableLeft.getIntrinsicWidth();
else if (drawableRight != null) {
drawableWidth = drawableRight.getIntrinsicWidth();
}
float bodyWidth = textWidth + drawableWidth + drawablePadding;
canvas.translate((getWidth() - bodyWidth) / 2, 0);
}
}
super.onDraw(canvas);
}
}
This is now available in the Material Button by default with the app:iconGravity property. However, the Material Button does not allow for setting the background to a drawable (RIP gradients).
I converted the answers by #BobDickinson and #David-Medenjak above to kotlin and it works great.
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.widget.AppCompatButton
import kotlin.math.max
class CenteredButton #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = R.attr.buttonStyle
) : AppCompatButton(context, attrs, defStyle) {
init {
gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
}
override fun onDraw(canvas: Canvas) {
val buttonContentWidth = (width - paddingLeft - paddingRight).toFloat()
var textWidth = 0f
layout?.let {
for (i in 0 until layout.lineCount) {
textWidth = max(textWidth, layout.getLineRight(i))
}
}
val drawableLeft = compoundDrawables[0]
val drawableWidth = drawableLeft?.intrinsicWidth ?: 0
val drawablePadding = if (textWidth > 0 && drawableLeft != null) compoundDrawablePadding else 0
val bodyWidth = textWidth + drawableWidth.toFloat() + drawablePadding.toFloat()
canvas.save()
canvas.translate((buttonContentWidth - bodyWidth) / 2, 0f)
super.onDraw(canvas)
canvas.restore()
}
}
I know it's a bit late, but if anyone looking for another answer, here is another way to add icon without the need to wrap button with a ViewGroup
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context=".MainActivity">
<Button
android:id="#+id/btnCamera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click!"
android:textAllCaps="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
*need to set textAllCaps to false to make the spannable working
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val buttonLabelBuilder = SpannableStringBuilder(btnCamera.text)
val iconDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_camera)
iconDrawable?.setBounds(0, 0, btnCamera.lineHeight, btnCamera.lineHeight)
val imageSpan = ImageSpan(iconDrawable, ImageSpan.ALIGN_BOTTOM)
buttonLabelBuilder.insert(0, "i ")
buttonLabelBuilder.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
btnCamera.text = buttonLabelBuilder
}
}
I started with #BobDickinson's answer, but it did not cope well with multiple lines. The approach is good, because you still end up with a Button that can properly be reused.
Here is an adapted solution that will also work if the button has multiple lines (Please don't ask why.)
Just extend Button and use the following in onDraw, the getLineRight() is used to look up the actual length of each line.
#Override
protected void onDraw(Canvas canvas) {
// We want the icon and/or text grouped together and centered as a group.
// We need to accommodate any existing padding
final float buttonContentWidth = getWidth() - getPaddingLeft() - getPaddingRight();
float textWidth = 0f;
final Layout layout = getLayout();
if (layout != null) {
for (int i = 0; i < layout.getLineCount(); i++) {
textWidth = Math.max(textWidth, layout.getLineRight(i));
}
}
// Compute left drawable width, if any
Drawable[] drawables = getCompoundDrawables();
Drawable drawableLeft = drawables[0];
int drawableWidth = (drawableLeft != null) ? drawableLeft.getIntrinsicWidth() : 0;
// We only count the drawable padding if there is both an icon and text
int drawablePadding = ((textWidth > 0) && (drawableLeft != null)) ? getCompoundDrawablePadding() : 0;
// Adjust contents to center
float bodyWidth = textWidth + drawableWidth + drawablePadding;
canvas.save();
canvas.translate((buttonContentWidth - bodyWidth) / 2, 0);
super.onDraw(canvas);
canvas.restore();
}
Here is a another solution:
<LinearLayout
android:id="#+id/llButton"
android:layout_width="match_parent"
style="#style/button_celeste"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
style="#style/button_celeste"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
android:clickable="false"
android:drawableLeft="#drawable/icon_phone"
android:text="#string/call_runid"/>
</LinearLayout>
and the event:
LinearLayout btnCall = (LinearLayout) findViewById(R.id.llButton);
btnCall.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
call(runid.Phone);
}
});
I had the same issue, and I've come up with a solution that doesn't require XML changes or custom Views.
This code snippet retrieves the width of the text and the left/right drawables, and sets the Button's left/right padding so there will only be enough space to draw the text and the drawables, and no more padding will be added.
This can be applied to Buttons as well as TextViews, their superclasses.
public class TextViewUtils {
private static final int[] LEFT_RIGHT_DRAWABLES = new int[]{0, 2};
public static void setPaddingForCompoundDrawableNextToText(final TextView textView) {
ViewTreeObserver vto = textView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
shinkRoomForHorizontalSpace(textView);
}
});
}
private static void shinkRoomForHorizontalSpace(TextView textView) {
int textWidth = getTextWidth(textView);
int sideCompoundDrawablesWidth = getSideCompoundDrawablesWidth(textView);
int contentWidth = textWidth + sideCompoundDrawablesWidth;
int innerWidth = getInnerWidth(textView);
int totalPadding = innerWidth - contentWidth;
textView.setPadding(totalPadding / 2, 0, totalPadding / 2, 0);
}
private static int getTextWidth(TextView textView) {
String text = textView.getText().toString();
Paint textPaint = textView.getPaint();
Rect bounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), bounds);
return bounds.width();
}
private static int getSideCompoundDrawablesWidth(TextView textView) {
int sideCompoundDrawablesWidth = 0;
Drawable[] drawables = textView.getCompoundDrawables();
for (int drawableIndex : LEFT_RIGHT_DRAWABLES) {
Drawable drawable = drawables[drawableIndex];
if (drawable == null)
continue;
int width = drawable.getBounds().width();
sideCompoundDrawablesWidth += width;
}
return sideCompoundDrawablesWidth;
}
private static int getInnerWidth(TextView textView) {
Rect backgroundPadding = new Rect();
textView.getBackground().getPadding(backgroundPadding);
return textView.getWidth() - backgroundPadding.left - backgroundPadding.right;
}
}
Notice that:
It actually still leaves some more space than needed (good enough for my purposes, but you may look for the error)
It overwrites whatever left/right padding is there. I guess it's not difficult to fix that.
To use it, just call TextViewUtils.setPaddingForCompoundDrawableNextToText(button) on your onCreate or onViewCreated().
There are several solutions to this problem. Perhaps the easiest on some devices is to use paddingRight and paddingLeft to move the image and text next to each other as below.
Original button
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="16dp"
android:text="#string/scan_qr_code"
android:textColor="#color/colorPrimary"
android:drawableLeft="#drawable/ic_camera"
android:paddingRight="90dp"
android:paddingLeft="90dp"
android:gravity="center"
/>
The problem here is on smaller devices this padding can cause unfortunate problems such as this:
The other solutions are all some version of "build a button out of a layout an image and a textview". They work, but completely emulating a button can be tricky. I propose one more solution; "build a button out of a layout an image, a textview, and a button"
Here's the same button rendered as I propose:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="16dp"
android:gravity="center"
>
<Button
android:id="#+id/scanQR"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/white_bg_button"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true"
android:gravity="center"
android:elevation="10dp"
>
<ImageView
android:id="#+id/scanImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:src="#drawable/ic_camera"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Button"
android:text="#string/scan_qr_code"
android:textColor="#color/colorPrimary"
/>
</LinearLayout>
</RelativeLayout>
As you can see, the button is now within a relative layout, but it's text and drawableLeft are not part of the button, they are in a separate layout that's placed on top of the button. With this, the button still acts like a button. The gotchas are:
The inner layout needs an elevation for newer versions of Android. The button itself has an elevation greater than the ImageView and TextView, so even though they are defined after the Button, they will still be "below" it in elevation and be invisible. Setting 'android:elevation' to 10 solves this.
The textAppearance of the TextView must be set so that it has the same appearance as it would in a button.
Another quite hacky alternative is to add blank spacer views with weight="1" on each side of the buttons. I don't know how this would affect performance.
<View
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1" />

How to have Image and Text Center within a Button

I want to display TEXT and Icon on a Button.
+----------------------------+
| Icon TEXT |
+----------------------------+
I tried with
<Button
android:id="#+id/Button01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="40dip"
android:text="TEXT"
android:drawableLeft="#drawable/Icon" />
But Text and Icon is not in center.
My Text size varies, according to text size Icon and Text should get adjusted to center.
How should i do it?
You can fake it by making a more complex layout, but I'm not sure whether it's worth it. Here's something I hacked together:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignTop="#+id/foreground"
android:layout_alignBottom="#id/foreground"
android:layout_alignRight="#id/foreground"
android:layout_alignLeft="#id/foreground"
android:onClick="clickedMe" />
<RelativeLayout
android:id="#id/foreground"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/hello" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#id/button_text"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:src="#drawable/icon" />
</RelativeLayout>
</RelativeLayout>
There might be a more concise way to do it. I tend to struggle getting RelativeLayout to do what I want sometimes. Note that you need to pay attention to the z-order (Button needs to appear first in the top level RelativeLayout) and you might need to adjust padding to get it to look the way you want.
Similar to some other approaches, I think a good solution is to extend Button and add the missing functionality by overriding its onLayout method:
public class CenteredIconButton extends Button {
private static final int LEFT = 0, TOP = 1, RIGHT = 2, BOTTOM = 3;
// Pre-allocate objects for layout measuring
private Rect textBounds = new Rect();
private Rect drawableBounds = new Rect();
public CenteredIconButton(Context context) {
this(context, null);
}
public CenteredIconButton(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.buttonStyle);
}
public CenteredIconButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) return;
final CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
TextPaint textPaint = getPaint();
textPaint.getTextBounds(text.toString(), 0, text.length(), textBounds);
} else {
textBounds.setEmpty();
}
final int width = getWidth() - (getPaddingLeft() + getPaddingRight());
final Drawable[] drawables = getCompoundDrawables();
if (drawables[LEFT] != null) {
drawables[LEFT].copyBounds(drawableBounds);
int leftOffset =
(width - (textBounds.width() + drawableBounds.width()) + getRightPaddingOffset()) / 2 - getCompoundDrawablePadding();
drawableBounds.offset(leftOffset, 0);
drawables[LEFT].setBounds(drawableBounds);
}
if (drawables[RIGHT] != null) {
drawables[RIGHT].copyBounds(drawableBounds);
int rightOffset =
((textBounds.width() + drawableBounds.width()) - width + getLeftPaddingOffset()) / 2 + getCompoundDrawablePadding();
drawableBounds.offset(rightOffset, 0);
drawables[RIGHT].setBounds(drawableBounds);
}
}
}
The sample only works for left and right drawables, but could be extended to adjust top and bottom drawables too.
How about this one?
<?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"
android:background="#color/lovely_color"
android:clickable="true"
android:onClick="clickHandler">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="no?"
android:textColor="#color/white"
android:layout_centerHorizontal="true"
android:drawableLeft="#drawable/lovely_icon"
android:drawablePadding="10dp"
android:padding="10dp"
android:gravity="center"
android:textSize="21sp"/>
</RelativeLayout>
This should work
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<TextView
android:id="#+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="hello" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="10dip"
/>
</LinearLayout>
How about using a SpannableString as the text with an ImageSpan?
Button myButton = ...
SpannableString ss = new SpannableString(" " + getString(R.string.my_button_text));
Drawable d = getResources().getDrawable(R.drawable.myIcon);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM);
ss.setSpan(span, 0, 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
myButton.setText(ss);
You can just set a padding depending on button size and image size:
Button button1 = null;
//initialize button….
ViewGroup.LayoutParams params = button1.getLayoutParams();
int btn1Width = ((int) (0.33 * (double)ecranWidth));
params.width = btn1Width;
button1.setLayoutParams(params);
button1.setPadding((btn1Width/2-9), 0, 0, 0);
//where (btn1Width/2-9) = size of button divided on 2 minux half size of icon…
The easy way (albeit not perfect) is to set the paddingRight to the same width as your icon.
<com.google.android.material.button.MaterialButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/your_text"
app:icon="#drawable/ic_example"
app:iconGravity="textStart"/>
This is what I did... It can be improved. The text is centered and the icon is to the left. So they both aren't centered as a group.
public class CustomButton extends Button
{
Rect r = new Rect();
private Drawable buttonIcon = null;
private int textImageSeparation = 10;
public CustomButton(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public CustomButton(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public CustomButton(Context context)
{
super(context);
}
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Drawable icon = getButtonIcon();
if(icon != null)
{
int drawableHeight = icon.getIntrinsicHeight();
int drawableWidth = icon.getIntrinsicWidth();
if(icon instanceof BitmapDrawable)
{
Bitmap bitmap = ((BitmapDrawable)icon).getBitmap();
drawableWidth = (int) AndroidScreenUtils.dipToPixels(bitmap.getWidth());
drawableHeight = (int) AndroidScreenUtils.dipToPixels(bitmap.getHeight());
}
else
{
drawableWidth = (int) AndroidScreenUtils.dipToPixels(icon.getIntrinsicWidth());
drawableHeight = (int) AndroidScreenUtils.dipToPixels(icon.getIntrinsicHeight());
}
float textWidth = getLayout().getPaint().measureText(getText().toString());
float left = ((getWidth() - textWidth) / 2) - getTextImageSeparation() - drawableWidth;
int height = getHeight();
int top = (height - drawableHeight) /2;
int right = (int) (left + drawableWidth);
int bottom = top + drawableHeight;
r.set((int) left, top, right, bottom);
icon.setBounds(r);
icon.draw(canvas);
}
}
private Drawable getButtonIcon()
{
return buttonIcon;
}
public void setButtonIcon(Drawable buttonIcon)
{
this.buttonIcon = buttonIcon;
}
private int getTextImageSeparation()
{
return textImageSeparation;
}
public void setTextImageSeparation(int dips)
{
this.textImageSeparation = (int) AndroidScreenUtils.dipToPixels(dips);
}
}
<LinearLayout
style="#style/Sonnen.Raised.Button.Transparent.LightBlueBorder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
android:orientation="horizontal"
android:padding="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableLeft="#drawable/refresh"
android:drawablePadding="10dp"
android:drawableStart="#drawable/refresh"
android:gravity="center"
android:text="#string/generic_error_button_text"
android:textColor="#color/dark_sky_blue"
android:textSize="20sp"/>
</LinearLayout>
I made a custom component to solve this problem.
Component class:
class CustomImageButton #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr, defStyleRes) {
init {
inflate(context, R.layout.custom_image_button, this)
// Load the styled attributes and set their properties
val typedArray = context.obtainStyledAttributes(
attrs,
R.styleable.CustomImageButton, defStyleAttr, 0
)
val src = typedArray?.getDrawable(R.styleable.CustomImageButton_cib_src)
val text = typedArray?.getText(R.styleable.CustomImageButton_cib_text)
val contentDescription = typedArray?.getText(R.styleable.CustomImageButton_cib_contentDescription)
ivIcon.setImageDrawable(src)
tvText.text = text
ivIcon.contentDescription = contentDescription
typedArray?.recycle()
}
}
Component XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:toos="http://schemas.android.com/tools"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="fill_parent"
android:layout_height="#dimen/button_height">
<Button
android:id="#+id/bClick"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignTop="#+id/foreground"
android:layout_alignBottom="#id/foreground"
android:layout_alignEnd="#id/foreground"
android:layout_alignStart="#id/foreground"/>
<RelativeLayout
android:id="#id/foreground"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#color/textWhite"
toos:text="Some text to test"
toos:ignore="RelativeOverlap"/>
<ImageView
android:id="#+id/ivIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="#id/tvText"
android:paddingTop="1dip"
android:paddingBottom="1dip"
android:src="#mipmap/some_image_to_test"
toos:ignore="ContentDescription"/>
</RelativeLayout>
</RelativeLayout>
The resources attributes, attrs.xml:
<declare-styleable name="CustomImageButton">
<attr name="cib_src" format="reference"/>
<attr name="cib_text" format="string"/>
<attr name="cib_contentDescription" format="string"/>
</declare-styleable>
Component use example:
<app.package.components.CustomImageButton
android:id="#+id/cibMyImageButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:cib_src="#mipmap/my_image_to_put_in_the_button"
app:cib_text="Some text to show in the button"
app:cib_contentDescription="icon description"/>
This is a hack, but worked for me with a negative margin:
<Button
android:id="#+id/some_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="#drawable/some_drawable"
android:drawablePadding="-118dp"
android:paddingEnd="28dp"
android:text="#string/some_string" />
A better way would probably be doing this in a custom view
android:layout_gravity="center_vertical|center_horizontal|center" >

Categories

Resources