如何通过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
相关产品推荐
相关产品推荐

