适合 CardView 的角 ImageView 不像 Android 中的 CardView 角那样具有半径

Corners ImageView fit inside CardView does not have radius like CardView's corners in Android

我正在开发一个 Android 应用程序。在我的应用程序中,我同时使用 CardViewImageView。但是我在 CardView 中设计 ImageView 时遇到了问题。问题是圆角半径为 ImageView

我有 XML 这样的适配器项目布局。

<?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>

如您所见,我将 CardView 的角半径设置为 5dp,并且 ImageView 适合其父级 CardView 的宽度。问题是 ImageView 的两个顶角都不像其父 CardView 的角那样弯曲。

这是截图

通常 CardView 的子视图的角在适合父 CardView 时会像其父角一样自动弯曲。那正确吗?那为什么我的 ImageView 不工作?

所以这是 pre lollipop 上的常见行为。以下是修复它的步骤:

第 1 步:将以下属性添加到您的 cardView

card_view:cardUseCompatPadding="true"
card_view:cardPreventCornerOverlap="false"
card_view:cardCornerRadius="10dp"

第 2 步:使用自定义 ImageView 使其顶部边框变圆:

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

}

第 3 步:只需将 xml 中的 ImageView 替换为 RoundedTopImageView

第 4 步:在您的代码中将其用作常规 imageView,例如 Picasso:

RoundedTopImageView image = (RoundedTopImageView) findViewById(R.id.di_iv_image);
Picasso.with(context)
                    .load("Some cool Url")
                    .into(image);

编辑:添加了convertDpToPixel函数

抱歉,我忘了添加这个,它是 Util class 的一部分,您可以添加到任何您想要的地方(在我的例子中是 DisplayUtils class):

public static int convertDpToPixel(int dp) {
    DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
}

Step1: 在你的App级别添加依赖build.gradle

compile 'com.makeramen:roundedimageview:2.2.1'

第二步:在CardView中xml代码:

<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 

android:id="@+id/di_card_container"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
app:cardCornerRadius="5dp"
app:cardElevation="4dp">

ImageView 中的第 3 步:

 <com.makeramen.roundedimageview.RoundedImageView
  android:id="@+id/di_iv_image"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:scaleType="centerCrop"
  android:layout_alignParentTop="true"
  app:riv_corner_radius_top_left="5dp"                                     
  app:riv_corner_radius_top_right="5dp"/>

希望对你有帮助

对于像我这样偶然发现这个问题的人..
如果您使用的是 Picasso,则可以使用 this transformations lib 并使用以下内容(在 Kotlin 中):

val radius = 5
Picasso.get()
        .load(image)
        .fit()        // to centerCrop, you have to do either resize() or fit()
        .centerCrop() // to remove any possible white areas
        .transform(RoundedCornersTransformation(radius, 0,
                RoundedCornersTransformation.CornerType.TOP))
        .into(imageView)

我尝试检查 radius 是 px 还是 dp,但找不到任何资源。不过,从我的测试来看,应该是dp。 The sample 似乎也暗示它是 dp,因为没有从 dp 到 px 的转换。

函数 fit()centerCrop()transform() 必须按顺序执行。

如果您使用的是 Glide,the Glide version of the lib 中也有类似的解决方案。