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

如何通过Glide实现图片上下区域聚焦缩放?

解决方案:Glide实现图片顶部/底部聚焦裁剪

一、修复自定义裁剪Transformation

你自行编写的BottomCropTransformation存在宽高比处理逻辑不全、圆角重复处理的问题,以下是优化后的版本,同时提供顶部裁剪的实现,适配所有宽高比场景,且避免与RoundedImageView的圆角逻辑冲突:

底部裁剪Transformation

public class BottomCropTransformation extends BitmapTransformation {
    private static final String ID = "com.yourpackage.BottomCropTransformation";
    private static final byte[] ID_BYTES = ID.getBytes(StandardCharsets.UTF_8);

    @Override
    protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        int sourceWidth = toTransform.getWidth();
        int sourceHeight = toTransform.getHeight();

        // 计算目标与原图的宽高比,确定裁剪区域
        float targetRatio = (float) outWidth / outHeight;
        float sourceRatio = (float) sourceWidth / sourceHeight;

        int cropX = 0;
        int cropY = 0;
        int cropWidth = sourceWidth;
        int cropHeight = sourceHeight;

        if (sourceRatio > targetRatio) {
            // 原图更宽,按目标高度裁剪宽度,居中裁剪左右
            cropWidth = (int) (sourceHeight * targetRatio);
            cropX = (sourceWidth - cropWidth) / 2;
        } else if (sourceRatio < targetRatio) {
            // 原图更高,按目标宽度裁剪高度,取底部区域
            cropHeight = (int) (sourceWidth / targetRatio);
            cropY = sourceHeight - cropHeight;
        }
        // 比例相等时无需裁剪

        // 优先从BitmapPool复用Bitmap,减少内存开销
        Bitmap result = pool.get(cropWidth, cropHeight, toTransform.getConfig());
        if (result == null) {
            result = Bitmap.createBitmap(cropWidth, cropHeight, toTransform.getConfig());
        }

        // 绘制裁剪后的区域到目标Bitmap
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setShader(new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        canvas.translate(-cropX, -cropY);
        canvas.drawRect(0, 0, cropWidth, cropHeight, paint);

        return result;
    }

    @Override
    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
        messageDigest.update(ID_BYTES);
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof BottomCropTransformation;
    }

    @Override
    public int hashCode() {
        return ID.hashCode();
    }
}

顶部裁剪Transformation

public class TopCropTransformation extends BitmapTransformation {
    private static final String ID = "com.yourpackage.TopCropTransformation";
    private static final byte[] ID_BYTES = ID.getBytes(StandardCharsets.UTF_8);

    @Override
    protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        int sourceWidth = toTransform.getWidth();
        int sourceHeight = toTransform.getHeight();

        float targetRatio = (float) outWidth / outHeight;
        float sourceRatio = (float) sourceWidth / sourceHeight;

        int cropX = 0;
        int cropY = 0;
        int cropWidth = sourceWidth;
        int cropHeight = sourceHeight;

        if (sourceRatio > targetRatio) {
            cropWidth = (int) (sourceHeight * targetRatio);
            cropX = (sourceWidth - cropWidth) / 2;
        } else if (sourceRatio < targetRatio) {
            cropHeight = (int) (sourceWidth / targetRatio);
            cropY = 0; // 取顶部区域
        }

        Bitmap result = pool.get(cropWidth, cropHeight, toTransform.getConfig());
        if (result == null) {
            result = Bitmap.createBitmap(cropWidth, cropHeight, toTransform.getConfig());
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setShader(new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        canvas.translate(-cropX, -cropY);
        canvas.drawRect(0, 0, cropWidth, cropHeight, paint);

        return result;
    }

    @Override
    public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
        messageDigest.update(ID_BYTES);
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof TopCropTransformation;
    }

    @Override
    public int hashCode() {
        return ID.hashCode();
    }
}

二、在Glide中应用Transformation

1. 调整布局的scaleType

将RoundedImageView的scaleType改为fitXY,因为Transformation已经处理了裁剪和缩放逻辑,ImageView只需填充容器即可:

<com.makeramen.roundedimageview.RoundedImageView
    android:id="@+id/slide_image"
    android:layout_width="match_parent"
    android:layout_height="@dimen/_182sdp"
    android:contentDescription="@string/todo"
    android:src="@drawable/alide"
    android:scaleType="fitXY"
    android:adjustViewBounds="true"
    app:riv_corner_radius="@dimen/_16sdp" />

2. 修改Glide加载代码

在RequestOptions中添加自定义的Transformation:

String image = mSliderItems.get(position).getImage();

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.ic_placeholder_full)
        .dontAnimate()
        .error(R.drawable.ic_placeholder_full)
        .transform(new BottomCropTransformation()); // 需要顶部裁剪则替换为new TopCropTransformation()

Glide.with(context)
        .load(image)
        .apply(options)
        .listener(new RequestListener<Drawable>() {
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                new Handler().postDelayed(() -> slideImageLoading(position, slide_image), 1000);
                return false;
            }

            @Override
            public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                return false;
            }
        })
        .into(slide_image);

三、使用成熟依赖库(更轻量高效)

如果不想自行维护Transformation代码,可以使用现成的glide-transformations库,它包含多种优化后的裁剪逻辑,适配RecyclerView场景:

1. 添加依赖

根据你的Glide版本选择对应库版本(Glide 4.x对应库版本4.x):

implementation 'jp.wasabeef:glide-transformations:4.3.0'

2. 直接使用库中实现

// 底部裁剪
RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.ic_placeholder_full)
        .dontAnimate()
        .error(R.drawable.ic_placeholder_full)
        .transform(new BottomCropTransformation());

// 顶部裁剪
// .transform(new TopCropTransformation());

注意事项

  • 无需在Transformation中处理圆角,RoundedImageView会负责圆角绘制,避免双重绘制影响性能
  • RecyclerView场景下保持Glide默认的内存/磁盘缓存开启,减少重复加载
  • 若图片显示仍不符合预期,可检查图片的实际宽高比是否与目标容器的宽高比匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:12:52