I am developing an Android App. In my app I am using CardView
and ImageView
together. But I am having a problem in designing ImageView
inside CardView
. The problem is with corner radius radius of ImageView
.
I have XML layout for adapter item like this.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
card_view:cardCornerRadius="5dp"
android:layout_width="match_parent"
android:id="@+id/di_card_container"
android:layout_height="wrap_content">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/di_iv_image"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:padding="10dp"
android:layout_below="@id/di_iv_image"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:id="@+id/di_name_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="15dp"
android:textColor="@color/textColorPrimary"
android:id="@+id/di_tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
As you can see I set, corner radius of CardView
to 5dp
and the ImageView
with is fit to the width of its parent CardView
. The problem is both top corners of ImageView
is not bent like its parent CardView
's corner.
This is the screenshot
Normally corners of child views of CardView
automatically bent like its parent corners if it fit to parent CardView
. Is that right? So why is my ImageView
is not working?
So this is a common behavior on pre lollipop. Here are the steps to fix it :
Step 1 : Add the following attributes to your cardView
card_view:cardUseCompatPadding="true"
card_view:cardPreventCornerOverlap="false"
card_view:cardCornerRadius="10dp"
Step 2 : Use a custom ImageView that rounds its top borders :
public class RoundedTopImageView extends ImageView {
private Paint mPaint;
private Path mPath;
private Bitmap mBitmap;
private Matrix mMatrix;
private int mRadius = DisplayUtils.convertDpToPixel(10);
private int mWidth;
private int mHeight;
private Drawable mDrawable;
public RoundedTopImageView(Context context) {
super(context);
init();
}
public RoundedTopImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedTopImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPath = new Path();
}
@Override
public void setImageDrawable(Drawable drawable) {
mDrawable = drawable;
if (drawable == null) {
return;
}
mBitmap = drawableToBitmap(drawable);
int bDIWidth = mBitmap.getWidth();
int bDIHeight = mBitmap.getHeight();
//Fit to screen.
float scale;
if ((mHeight / (float)bDIHeight) >= (mWidth / (float)bDIWidth)){
scale = mHeight / (float)bDIHeight;
} else {
scale = mWidth / (float)bDIWidth;
}
float borderLeft = (mWidth - (bDIWidth * scale)) / 2;
float borderTop = (mHeight - (bDIHeight * scale)) / 2;
mMatrix = getImageMatrix();
RectF drawableRect = new RectF(0, 0, bDIWidth, bDIHeight);
RectF viewRect = new RectF(borderLeft, borderTop, (bDIWidth * scale) + borderLeft, (bDIHeight * scale) + borderTop);
mMatrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER);
invalidate();
}
private Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
} else {
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;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
if ((mDrawable != null) && (mHeight > 0) && (mWidth > 0)) {
setImageDrawable(mDrawable);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap == null) {
return;
}
canvas.drawColor(Color.TRANSPARENT);
mPath.reset();
mPath.moveTo(0, mRadius);
mPath.lineTo(0, canvas.getHeight());
mPath.lineTo(canvas.getWidth(), canvas.getHeight());
mPath.lineTo(canvas.getWidth(), mRadius);
mPath.quadTo(canvas.getWidth(), 0, canvas.getWidth() - mRadius, 0);
mPath.lineTo(mRadius, 0);
mPath.quadTo(0, 0, 0, mRadius);
canvas.drawPath(mPath, mPaint);
canvas.clipPath(mPath);
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}
}
Step 3 : Simply replace your ImageView in your xml with the RoundedTopImageView
Step 4 : Use this in your code as a regular imageView, for example with Picasso :
RoundedTopImageView image = (RoundedTopImageView) findViewById(R.id.di_iv_image);
Picasso.with(context)
.load("Some cool Url")
.into(image);
EDIT : Added the convertDpToPixel
function
Sorry, I forgot to add this, which is part of a Util class that you can add anywhere you want (in my case in DisplayUtils
class) :
public static int convertDpToPixel(int dp) {
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
}