Glide如何创建带弧形边缘(非圆形)的自定义Transformation
嘿,我来帮你搞定这个带弧形边缘的方形Transformation!你之前踩的坑应该是没把圆角计算和Canvas绘制的细节做对,我给你一套完整的实现方案,包含Bitmap处理和占位Drawable的适配,包你满意~
实现带弧形边缘的方形Transformation(Glide)
核心需求是保留方形的整体形状,只把四个角改成圆弧(区别于CircleCrop的全圆形裁剪)。下面分两部分实现:Bitmap的Transformation(处理加载的图片)和占位Drawable的适配,同时解决你提到的计算问题。
一、自定义RoundEdgesTransformation(处理Bitmap)
这个类继承Glide的BitmapTransformation,用更兼容硬件加速的Xfermode方案实现,避免clipPath的兼容性问题:
import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import androidx.annotation.NonNull; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Objects; public class RoundEdgesTransformation extends BitmapTransformation { private final float radius; // 圆角半径(像素值) // 构造函数:传入圆角半径(建议先转成像素再传入,比如从dp转px) public RoundEdgesTransformation(float radius) { this.radius = radius; } @Override protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { int width = toTransform.getWidth(); int height = toTransform.getHeight(); // 从Glide的BitmapPool获取Bitmap,避免内存浪费 Bitmap result = pool.get(width, height, Bitmap.Config.ARGB_8888); if (result == null) { result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(result); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // 第一步:画一个圆角矩形作为遮罩 RectF rectF = new RectF(0, 0, width, height); canvas.drawRoundRect(rectF, radius, radius, paint); // 第二步:设置混合模式,只保留遮罩范围内的原Bitmap内容 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(toTransform, 0, 0, paint); paint.setXfermode(null); // 用完记得清空Xfermode return result; } // 以下三个方法是Glide Transformation的必要重写,用于缓存和判断相等性 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RoundEdgesTransformation that = (RoundEdgesTransformation) o; return Float.compare(that.radius, radius) == 0; } @Override public int hashCode() { return Objects.hash(radius); } @Override public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { messageDigest.update(("RoundEdgesTransformation_" + radius).getBytes(StandardCharsets.UTF_8)); } }
关键计算细节说明
- 圆角半径的单位:一定要传入像素值,如果你的设计稿给的是dp,需要先转换:
float radiusDp = 30f; // 比如设计稿要求30dp的圆角 float radiusPx = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, radiusDp, getResources().getDisplayMetrics() ); - RectF的范围:直接用原Bitmap的宽高作为矩形范围,确保圆角覆盖整个方形的四个角。
- Xfermode的作用:
SRC_IN模式会让原Bitmap只显示在遮罩(圆角矩形)的范围内,完美实现弧形边缘的效果。
二、适配占位Drawable
如果你需要给占位Drawable也加上相同的弧形边缘,可以自定义一个RoundedCornerDrawable:
import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.BitmapShader; import android.graphics.drawable.Drawable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; public class RoundedCornerDrawable extends Drawable { private final Paint paint; private final Bitmap bitmap; private final float radius; private final RectF rectF = new RectF(); public RoundedCornerDrawable(Bitmap bitmap, float radius) { this.bitmap = bitmap; this.radius = radius; paint = new Paint(); paint.setAntiAlias(true); // 用BitmapShader确保Drawable内容填充到圆角矩形内,不会拉伸变形 paint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); rectF.set(bounds); // 跟随View的边界更新矩形范围 } @Override public void draw(@NonNull Canvas canvas) { canvas.drawRoundRect(rectF, radius, radius, paint); } @Override public void setAlpha(int alpha) { paint.setAlpha(alpha); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { paint.setColorFilter(colorFilter); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } }
使用方式
// 加载图片时应用Transformation Glide.with(this) .load("你的图片URL或本地路径") .transform(new RoundEdgesTransformation(radiusPx)) .into(imageView); // 设置占位Drawable Bitmap placeholderBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_placeholder); Drawable roundedPlaceholder = new RoundedCornerDrawable(placeholderBitmap, radiusPx); Glide.with(this) .load("你的图片URL或本地路径") .placeholder(roundedPlaceholder) .transform(new RoundEdgesTransformation(radiusPx)) .into(imageView);
为什么你之前的尝试没成功?
大概率是这几个细节没处理好:
- 直接用dp作为圆角半径,没有转成像素,导致在不同分辨率设备上效果不一致;
- 没有用
BitmapShader或Xfermode,直接画圆角矩形会导致图片拉伸变形; - 使用
clipPath时没有考虑硬件加速的兼容性,部分设备上会出现裁剪失效的问题; - 没有正确利用Glide的
BitmapPool,可能导致内存浪费或OOM。
内容的提问来源于stack exchange,提问作者Shubham AgaRwal
相关产品推荐
相关产品推荐

