Android 玩转Glide4---Transformation篇

Githa ·
更新时间:2024-11-11
· 679 次阅读

前言

系列文章专栏: 玩转Glide4
基础使用篇:Android 玩转Glide4—基础使用篇
进阶使用篇:Android 玩转Glide4—进阶使用篇
Transformation篇:Android 玩转Glide4—Transformation篇

概述

再基础篇和进阶篇中,我们简单介绍了Glide4的用法,和一些进阶的使用。
本篇Transformation转换篇,将给大家介绍Glide4强大的转换功能。

Glide自带的转换效果 Glide.with(this).load(ConstUrl.ImgUrl).into(ivScaleType)

如上代码,经常使用Glide的会发现一个问题:
对于一个宽高自适应的ImageView,并且不指定scaleType,即使加载的图片分辨率小于手机屏幕,图的宽度还是会铺满屏幕。
如下所示:
在这里插入图片描述

为什么会这样呢?

我们看一下into()方法的源码:

public ViewTarget into(@NonNull ImageView view) { Util.assertMainThread(); Preconditions.checkNotNull(view); RequestOptions requestOptions = this.requestOptions; 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. } } return into( glideContext.buildImageViewTarget(view, transcodeClass), /*targetListener=*/ null, requestOptions); }

通过源码我们发现,Glide会获取ImageView的scaleType,进行centerCrop(),fitCenter(),centerInside()三种转换(这三个方法我们也可以RequestOptions直接调用)。
而我们知道在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER。

深究的Glide源码,会发现最后执行了fitCenter()方法,如果原图的宽高与ImageView的一致,不做任何改变,否则就是在保持纵横比不变的情况下对图片进行缩放。

public static Bitmap fitCenter(@NonNull BitmapPool pool, @NonNull Bitmap inBitmap, int width, int height) { if (inBitmap.getWidth() == width && inBitmap.getHeight() == height) { return inBitmap; } final float widthPercentage = width / (float) inBitmap.getWidth(); final float heightPercentage = height / (float) inBitmap.getHeight(); final float minPercentage = Math.min(widthPercentage, heightPercentage); // Round here in case we've decoded exactly the image we want, but take the floor below to // avoid a line of garbage or blank pixels in images. int targetWidth = Math.round(minPercentage * inBitmap.getWidth()); int targetHeight = Math.round(minPercentage * inBitmap.getHeight()); if (inBitmap.getWidth() == targetWidth && inBitmap.getHeight() == targetHeight) { return inBitmap; } // Take the floor of the target width/height, not round. If the matrix // passed into drawBitmap rounds differently, we want to slightly // overdraw, not underdraw, to avoid artifacts from bitmap reuse. targetWidth = (int) (minPercentage * inBitmap.getWidth()); targetHeight = (int) (minPercentage * inBitmap.getHeight()); Bitmap.Config config = getNonNullConfig(inBitmap); Bitmap toReuse = pool.get(targetWidth, targetHeight, config); // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given. TransformationUtils.setAlpha(inBitmap, toReuse); Matrix matrix = new Matrix(); matrix.setScale(minPercentage, minPercentage); applyMatrix(inBitmap, toReuse, matrix); return toReuse; }

centerInside(),centerCrop()的源码就不贴出来,有兴趣的可以自己查看。

方法 说明
fitCenter() 如果原图的宽高与ImageView的一致,不做任何改变;否则就是在保持纵横比不变的情况下对图片进行居中缩放。默认。
centerInside() 如果原图的宽高小于ImageView的宽高,则不做任何改变;否则执行fitCenter()
centerCrop() 如果原图的宽高小于ImageView的宽高,则不做任何改变;否则就缩放图像,以使图像的宽度与给定的宽度匹配,并且的高度大于图像的给定高度,反之亦然,然后裁剪较大的尺寸以与给定的尺寸匹配
那如何加载图片的原始尺寸呢?

在Glide3中我们可以使用dontTransform()方法,表示Glide在加载图片的过程中不进行图片变换。
但是在Glide4中该方法只会停止transform方法传入的转换,不再影响fitCenter(),centerInside(),centerCrop()。
而且dontTransform()会停止所有的变换操作,显然是不够的。

这种情况下我们只需要借助override()方法强制将图片尺寸指定成原始大小就可以了。

val optionsScaleType = RequestOptions().override(Target.SIZE_ORIGINAL) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsScaleType).into(ivScaleType) 圆形图片

圆形图片是app开发中是最常见需求,以往我们会使用自定义View的圆形图片,在Glide4中,一个方法即可实现。

//圆形转换 val optionsCircle = RequestOptions().circleCrop() Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsCircle).into(ivCircle)

在这里插入图片描述

Glide Transformations

借助Glide Transformations库,我们可以非常轻松的实现各种基本的图片变换,如裁剪变换、颜色变换、模糊变换等等。
如果你要更加高级的变换,比如GPU渲染等,它也能实现。基本能满足我们所有的日常开发需求。

库引用 repositories { jcenter() } dependencies { implementation 'jp.wasabeef:glide-transformations:4.x.x' // If you want to use the GPU Filters implementation 'jp.co.cyberagent.android:gpuimage:2.x.x' } 基本转换

由于Glide4自带圆形转换方法circleCrop(),所以CropCircleTransformation就被废弃了。

高斯模糊

可传入模糊度,采样率两个参数。

public BlurTransformation() { this(MAX_RADIUS, DEFAULT_DOWN_SAMPLING); } public BlurTransformation(int radius) { this(radius, DEFAULT_DOWN_SAMPLING); } public BlurTransformation(int radius, int sampling) { this.radius = radius; this.sampling = sampling; } //高斯模糊 val optionsBlur = RequestOptions().transform(BlurTransformation(15, 5)) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsBlur).into(ivBlur) 圆角矩形

可传入角度,外边距,圆角类型三个三个参数。
圆角矩形的CornerType,枚举了所有的可能性。

public enum CornerType { ALL, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, TOP, BOTTOM, LEFT, RIGHT, OTHER_TOP_LEFT, OTHER_TOP_RIGHT, OTHER_BOTTOM_LEFT, OTHER_BOTTOM_RIGHT, DIAGONAL_FROM_TOP_LEFT, DIAGONAL_FROM_TOP_RIGHT } public RoundedCornersTransformation(int radius, int margin) { this(radius, margin, CornerType.ALL); } public RoundedCornersTransformation(int radius, int margin, CornerType cornerType) { this.radius = radius; this.diameter = this.radius * 2; this.margin = margin; this.cornerType = cornerType; } //圆角矩形 val optionsRounded = RequestOptions().transform(RoundedCornersTransformation()) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsRounded).into(ivRounded) 灰度转换

ORZ…让我想起了前端时间清明节的首页灰度。

//灰度转换 val optionsGray = RequestOptions().transform(GrayscaleTransformation()) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsGray).into(ivGray) 裁剪变换 public enum CropType { TOP, CENTER, BOTTOM } private int width; private int height; private CropType cropType = CropType.CENTER; public CropTransformation(int width, int height) { this(width, height, CropType.CENTER); } public CropTransformation(int width, int height, CropType cropType) { this.width = width; this.height = height; this.cropType = cropType; } //裁剪转换 val optionsCrop = RequestOptions().transform(CropTransformation(200, 100, CropTransformation.CropType.TOP)) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsCrop).into(ivCrop) //正方形裁剪 val optionsSquare = RequestOptions().transform(CropSquareTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSquare).into(ivSquare) 图形变换

这个就比较厉害了。

保留覆盖目标像素的源像素,丢弃其余的源像素和目标像素。

就是指定一个资源文件作为目标形状,加载出来的图片文件形状跟资源文件的形状完全一致。
这个转换理论上可以将图片加载为任何形状。

//图形变换 val optionsMask = RequestOptions().transform(MaskTransformation(R.drawable.mask_starfish)) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsMask).into(ivMask) val optionsMask1 = RequestOptions().transform(MaskTransformation(R.drawable.x)) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsMask1).into(ivMask1)

在这里插入图片描述

GPU转换

GPU渲染效果转换,非常炫酷屌炸天。
我按照源码的顺序都实现了一遍,直接看效果图。

//高亮效果 val optionsBrightness = RequestOptions().transform(BrightnessFilterTransformation(0.3f)) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsBrightness).into(ivBrightness) //滤镜效果 val optionsContrast = RequestOptions().transform(ContrastFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsContrast).into(ivContrast) //虚幻效果 val optionsInvert = RequestOptions().transform(InvertFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsInvert).into(ivInvert) //马赛克效果 val optionsKuwahara = RequestOptions().transform(KuwaharaFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsKuwahara).into(ivKuwahara) //像素效果 val optionsPixelation = RequestOptions().transform(PixelationFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsPixelation).into(ivPixelation) //漫画效果 val optionsSepia = RequestOptions().transform(SepiaFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSepia).into(ivSepia) //铅笔画效果 val optionsSketch = RequestOptions().transform(SketchFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSketch).into(ivSketch) //漩涡效果 val optionsSwirl = RequestOptions().transform(SwirlFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsSwirl).into(ivSwirl) //油画效果 val optionsToon = RequestOptions().transform(ToonFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsToon).into(ivToon) //暗边效果 val optionsVignette = RequestOptions().transform(VignetteFilterTransformation()) Glide.with(this).load(ConstUrl.ImgOne).apply(optionsVignette).into(ivVignette)

在这里插入图片描述

自定义转换

当然这些肯定不可能满足所有的需求,比如我要实现一个聊天头饰效果,在所有的用户头像上加一个如下图的头饰效果。
在这里插入图片描述
我们想一下有了图形变换MaskTransformation我们基本可以实现所有的形状变换,MaskTransformation是在目标资源文件上绘制。
而我们这个需求是需要在目标资源文件下绘制,即将目标资源文件覆盖在加载图片上方。
分析需求后我们只需要修改MaskTransformation其中一行代码设置为xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER),即可实现将一个将图片覆盖在另一个图片上的需求需求。

PorterDuff.Mode有很多种模式,在Android低层的graphics包里面,有兴趣的同学多了解。

因此修改后的代码几乎跟MaskTransformation一致,如下:

class HeaddressTransformation constructor(private val maskId: Int) : BitmapTransformation() { private val VERSION = 1 private val ID = "com.demon.glide4img.HeaddressTransformation.$VERSION" private val sMaskingPaint by lazy { Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER) } } override fun hashCode(): Int { return ID.hashCode() + maskId * 10 } override fun equals(other: Any?): Boolean { return other is HeaddressTransformation && other.maskId == maskId } override fun updateDiskCacheKey(messageDigest: MessageDigest) { messageDigest.update((ID + maskId).toByteArray(Key.CHARSET)) } override fun transform(context: Context, pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap { val width = toTransform.width val height = toTransform.height val result: Bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888) Canvas(result).run { val mask = Utils.getMaskDrawable(context.applicationContext, maskId) mask.setBounds(0, 0, width, height) mask.draw(this) drawBitmap(toTransform, 0f, 0f, sMaskingPaint) } return result } }

由于头饰是圆形的,我们又需要将加载的图片转换为圆形效果。RequestOptions的transforms方法可以同事加载多个Transformation,进行复合变换。

public RequestOptions transforms(@NonNull Transformation... transformations) { return transform(new MultiTransformation(transformations), /*isRequired=*/ true); }

最后使用如下:

//圆形头饰效果 val optionsHeaddress = RequestOptions().centerCrop().transforms(CropCircleTransformation(), HeaddressTransformation(R.drawable.pic_kehu_hg)) Glide.with(this).load(ConstUrl.ImgUrl).apply(optionsHeaddress).into(ivHead)

在这里插入图片描述

代码

GitHub: https://github.com/DeMonDemo/Glide4Img

参考

郭霖的专栏—Glide最全解析


作者:DeMonnnnnn



glide Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号