在使用Glide4.X的时候,经常会使用到图片变换的情况,都是网上搜索一番,Ctrl + C + Ctrl + V,没有系统地去看过这一块东西,网上的资料也很零散,有时候遇到一些较复杂的情况,得搜索好久才能得到自己想要的结果。出现这个痛点后,现准备针对Transformation罗列记录一下。
本文Glide版本为4.13.2
implementation 'com.github.bumptech.glide:glide:4.13.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'
文中的布局是一个固定宽高300*300的ImageView
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_view"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
androidx.constraintlayout.widget.ConstraintLayout>
正常显示photo1效果如下

Transformation(转换器),这个类可以说是 Glide 压缩裁剪图片的核心类,因为该类功能就是依据要求输出的宽高对原始资源进行压缩裁剪之类的转换
public interface Transformation<T> extends Key {
/**
* 转换原始资源并返回转换后的资源对象
*/
Resource<T> transform(@NonNull Context context, @NonNull Resource<T> resource,
int outWidth, int outHeight);
}
我们使用Android Studio自带的Hierarchy工具,可以查看到,实现Transformations接口的类总共有这些

Hierarchy工具的具体使用,详见我的另一篇博客 : Android Studio 使用 自带的Hierarchy查看类/方法/调用的层级关系
BitmapTransformation有很多子类,这里是我们关注的重点
其中,Glide中的CenterCrop、fitCenter、CenterInside和ImageView的scale type有对应关系,对照关系如下
| Glide | ImageView的scaleType |
|---|---|
| CenterCrop | centerCrop |
| fitCenter | fitCenter |
| CenterInside | centerInside 和 fitXY |
关于
ImageView scaleType可以查看这篇文章 Android ImageView 的scaleType 属性图解
从代码中也可以看出这种对照关系 RequestBuilder#into()
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
这个就是对图片裁剪为圆形
Glide.with(this)
.load(R.drawable.photo1)
.circleCrop()
.into(imageView)
效果如下

这个就是对图片进行旋转操作
Glide.with(this)
.load(R.drawable.photo1)
.transform(Rotate(90))
.into(imageView)

这个是对图片进行圆角操作
Glide.with(this)
.load(R.drawable.photo1)
.transform(RoundedCorners(ConvertUtils.dp2px(30F)))
.into(imageView)
效果如下

这个是当上下左右四个圆角值不一样的时候,对每个圆角的值进行定义
val radius30 = ConvertUtils.dp2px(30F).toFloat()
val radius5 = ConvertUtils.dp2px(5F).toFloat()
Glide.with(this)
.load(R.drawable.photo1)
.transform(GranularRoundedCorners(radius30, radius5, radius30, radius5))
.into(imageView)
效果如下

默认情况下,每个 transform() 调用,或任何特定转换方法fitCenter(), centerCrop(), bitmapTransform()的调用都会替换掉之前的变换。
如果想在单次加载中应用多个变换,需要使用使用 MultiTransformation 类。
Glide.with(this)
.load(R.drawable.photo1)
.transform(MultiTransformation(CenterCrop(),Rotate(90)))
.into(imageView)
效果如下

不做任何处理,直接返回
@NonNull
@Override
public Resource<T> transform(
@NonNull Context context, @NonNull Resource<T> resource, int outWidth, int outHeight) {
return resource;
}
将Bitmap转化为Drawable,在源码里有用到,我们一般不会去调用。
注解上说已弃用,建议使用DrawableTransformation
Github上有一个Glide的Transformation库,glide-transformations,提供了一些额外的Transformation
需要添加如下依赖
implementation 'jp.wasabeef:glide-transformations:4.3.0'
// 如果想使用GPU Filter,需要这个依赖,对应着文中的4.13-4.22部分
implementation 'jp.co.cyberagent.android:gpuimage:2.1.0'
Glide.with(context)
.load(R.drawable.photo1)
.apply(overrideOf(266.px, 252.px))
.apply(bitmapTransform(MultiTransformation<Bitmap>(CenterCrop(),
MaskTransformation(R.drawable.mask_starfish))))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(overrideOf(300.px, 200.px))
.apply(bitmapTransform(MultiTransformation<Bitmap>(CenterCrop(),
MaskTransformation(R.drawable.mask_chat_right))))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(RoundedCornersTransformation(120, 0,
RoundedCornersTransformation.CornerType.DIAGONAL_FROM_TOP_LEFT)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(CropTransformation(300.px, 100.px, CropType.TOP)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(CropTransformation(300.px, 100.px, CropType.CENTER)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(CropTransformation(300.px, 100.px, CropType.BOTTOM)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(CropSquareTransformation()))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(CropCircleTransformation()))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(
CropCircleWithBorderTransformation(Utils.toDp(4), Color.rgb(0, 145, 86))))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(GrayscaleTransformation()))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(BlurTransformation(25)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(BlurTransformation(25, 8)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(ToonFilterTransformation()))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(SepiaFilterTransformation()))
.into(holder.image)

Glide.with(context)
.load(R.drawable.check)
.apply(bitmapTransform(ContrastFilterTransformation(2.0f)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(ContrastFilterTransformation(2.0f)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(PixelationFilterTransformation(20f)))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(SketchFilterTransformation()))
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(
SwirlFilterTransformation(0.5f, 1.0f, PointF(0.5f, 0.5f))).dontAnimate())
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(BrightnessFilterTransformation(0.5f)).dontAnimate())
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(KuwaharaFilterTransformation(25)).dontAnimate())
.into(holder.image)

Glide.with(context)
.load(R.drawable.photo1)
.apply(bitmapTransform(VignetteFilterTransformation(PointF(0.5f, 0.5f),
floatArrayOf(0.0f, 0.0f, 0.0f), 0f, 0.75f)).dontAnimate())
.into(holder.image)

如果常用的Transformation满足不了我们的需要,也可以自定义Transformation
如果只需要变换 Bitmap,最好是从继承 BitmapTransformation 开始。BitmapTransformation 为我们处理了一些基础的东西,例如,如果变换返回了一个新修改的 Bitmap ,BitmapTransformation将负责提取和回收原始的 Bitmap。
比如,我们自己去实现一个旋转图片的Transformation
class RotateTransformation(private val angle: Float) : BitmapTransformation() {
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
}
override fun transform(
pool: BitmapPool,
toTransform: Bitmap,
outWidth: Int,
outHeight: Int
): Bitmap {
val matrix = Matrix()
matrix.postRotate(angle)
return Bitmap.createBitmap(
toTransform,
0,
0,
toTransform.width,
toTransform.height,
matrix,
true
)
}
}
进行使用
Glide.with(this)
.load(R.mipmap.photo1)
.apply(bitmapTransform(RotateTransformation(90F)))
.into(imageView)
效果如下
