You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

为什么你之前的尝试没成功?

大概率是这几个细节没处理好:

  1. 直接用dp作为圆角半径,没有转成像素,导致在不同分辨率设备上效果不一致;
  2. 没有用BitmapShader或Xfermode,直接画圆角矩形会导致图片拉伸变形;
  3. 使用clipPath时没有考虑硬件加速的兼容性,部分设备上会出现裁剪失效的问题;
  4. 没有正确利用Glide的BitmapPool,可能导致内存浪费或OOM。

内容的提问来源于stack exchange,提问作者Shubham AgaRwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:07:16