Glide源码学习六:图片变换(1),2024年最新面试官流程

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

Glide.with(this)

.load(url)

.transform(…)

.into(imageView);

至于具体要进行什么样的图片变换操作,这个通常都是需要我们自己来写的。不过Glide已经内置了两种图片变换操作,我们可以直接拿来使用,一个是CenterCrop,一个是FitCenter。

但这两种内置的图片变换操作其实都不需要使用transform()方法,Glide为了方便我们使用直接提供了现成的API:

Glide.with(this)

.load(url)

.centerCrop()

.into(imageView);

Glide.with(this)

.load(url)

.fitCenter()

.into(imageView);

当然,centerCrop()和fitCenter()方法其实也只是对transform()方法进行了一层封装而已,它们背后的源码仍然还是借助transform()方法来实现的,如下所示:

public class DrawableRequestBuilder

extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>

implements BitmapOptions, DrawableOptions {

/**

  • Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.

  • @see #fitCenter()

  • @see #transform(BitmapTransformation…)

  • @see #bitmapTransform(Transformation[])

  • @see #transform(Transformation[])

  • @return This request builder.

*/

@SuppressWarnings(“unchecked”)

public DrawableRequestBuilder centerCrop() {

return transform(glide.getDrawableCenterCrop());

}

/**

  • Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.

  • @see #centerCrop()

  • @see #transform(BitmapTransformation…)

  • @see #bitmapTransform(Transformation[])

  • @see #transform(Transformation[])

  • @return This request builder.

*/

@SuppressWarnings(“unchecked”)

public DrawableRequestBuilder fitCenter() {

return transform(glide.getDrawableFitCenter());

}

}

那么这两种内置的图片变换操作到底能实现什么样的效果呢?FitCenter的效果其实刚才我们已经见识过了,就是会将图片按照原始的长宽比充满全屏。那么CenterCrop又是什么样的效果呢?我们来动手试一下就知道了。

为了让效果更加明显,这里我就不使用百度首页的Logo图了,而是换成必应首页的一张美图。在不应用任何图片变换的情况下,使用Glide加载必应这张图片效果如下所示。

现在我们添加一个CenterCrop的图片变换操作,代码如下:

String url = “http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg”;

Glide.with(this)

.load(url)

.centerCrop()

.into(imageView);

重新运行一下程序并点击加载图片按钮,效果如下图所示。

可以看到,现在展示的图片是对原图的中心区域进行裁剪后得到的图片。

另外,centerCrop()方法还可以配合override()方法来实现更加丰富的效果,比如指定图片裁剪的比例:

String url = “http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg”;

Glide.with(this)

.load(url)

.override(500, 500)

.centerCrop()

.into(imageView);

可以看到,这里我们将图片的尺寸设定为500*500像素,那么裁剪的比例也就变成1:1了,现在重新运行一下程序,效果如下图所示。

这样我们就把Glide内置的图片变换接口的用法都掌握了。不过不得不说,Glide内置的图片变换接口功能十分单一且有限,完全没有办法满足我们平时的开发需求。因此,掌握自定义图片变换功能就显得尤为重要了。

不过,在正式开始学习自定义图片变换功能之前,我们先来探究一下CenterCrop这种图片变换的源码,理解了它的源码我们再来进行自定义图片变换就能更加得心应手了。

源码分析

====

那么就话不多说,我们直接打开CenterCrop类来看一下它的源码吧,如下所示:

public class CenterCrop extends BitmapTransformation {

public CenterCrop(Context context) {

super(context);

}

public CenterCrop(BitmapPool bitmapPool) {

super(bitmapPool);

}

// Bitmap doesn’t implement equals, so == and .equals are equivalent here.

@SuppressWarnings(“PMD.CompareObjectsWithEquals”)

@Override

protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {

final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null

? toTransform.getConfig() : Bitmap.Config.ARGB_8888);

Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);

if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {

toReuse.recycle();

}

return transformed;

}

@Override

public String getId() {

return “CenterCrop.com.bumptech.glide.load.resource.bitmap”;

}

}

这段代码并不长,但是我还是要划下重点,这样大家看起来的时候会更加轻松。

首先,CenterCrop是继承自BitmapTransformation的,这个是重中之重,因为整个图片变换功能都是建立在这个继承结构基础上的。

接下来CenterCrop中最重要的就是transform()方法,其他的方法我们可以暂时忽略。transform()方法中有四个参数,每一个都很重要,我们来一一解读下。第一个参数pool,这个是Glide中的一个Bitmap缓存池,用于对Bitmap对象进行重用,否则每次图片变换都重新创建Bitmap对象将会非常消耗内存。第二个参数toTransform,这个是原始图片的Bitmap对象,我们就是要对它来进行图片变换。第三和第四个参数比较简单,分别代表图片变换后的宽度和高度,其实也就是override()方法中传入的宽和高的值了。

下面我们来看一下transform()方法的细节,首先第一行就从Bitmap缓存池中尝试获取一个可重用的Bitmap对象,然后把这个对象连同toTransform、outWidth、outHeight参数一起传入到了TransformationUtils.centerCrop()方法当中。那么我们就跟进去来看一下这个方法的源码,如下所示:

public final class TransformationUtils {

public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {

if (toCrop == null) {

return null;

} else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {

return toCrop;

}

// From ImageView/Bitmap.createScaledBitmap.

final float scale;

float dx = 0, dy = 0;

Matrix m = new Matrix();

if (toCrop.getWidth() * height > width * toCrop.getHeight()) {

scale = (float) height / (float) toCrop.getHeight();

dx = (width - toCrop.getWidth() * scale) * 0.5f;

} else {

scale = (float) width / (float) toCrop.getWidth();

dy = (height - toCrop.getHeight() * scale) * 0.5f;

}

m.setScale(scale, scale);

m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));

final Bitmap result;

if (recycled != null) {

result = recycled;

} else {

result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));

}

// We don’t add or remove alpha, so keep the alpha setting of the Bitmap we were given.

TransformationUtils.setAlpha(toCrop, result);

Canvas canvas = new Canvas(result);

Paint paint = new Paint(PAINT_FLAGS);

canvas.drawBitmap(toCrop, m, paint);

return result;

}

}

这段代码就是整个图片变换功能的核心代码了。可以看到,第5-9行主要是先做了一些校验,如果原图为空,或者原图的尺寸和目标裁剪尺寸相同,那么就放弃裁剪。接下来第11-22行是通过数学计算来算出画布的缩放的比例以及偏移值。第24-29行是判断缓存池中取出的Bitmap对象是否为空,如果不为空就可以直接使用,如果为空则要创建一个新的Bitmap对象。第32行是将原图Bitmap对象的alpha值复制到裁剪Bitmap对象上面。最后第34-37行是裁剪Bitmap对象进行绘制,并将最终的结果进行返回。全部的逻辑就是这样,总体来说还是比较简单的,可能也就是数学计算那边需要稍微动下脑筋。

那么现在得到了裁剪后的Bitmap对象,我们再回到CenterCrop当中,你会看到,在最终返回这个Bitmap对象之前,还会尝试将复用的Bitmap对象重新放回到缓存池当中,以便下次继续使用。

好的,这样我们就将CenterCrop图片变换的工作原理完整地分析了一遍,FitCenter的源码也是基本类似的,这里就不再重复分析了。了解了这些内容之后,接下来我们就可以开始学习自定义图片变换功能了。

自定义图片变换

=======

Glide给我们定制好了一个图片变换的框架,大致的流程是我们可以获取到原始的图片,然后对图片进行变换,再将变换完成后的图片返回给Glide,最终由Glide将图片显示出来。理论上,在对图片进行变换这个步骤中我们可以进行任何的操作,你想对图片怎么样都可以。包括圆角化、圆形化、黑白化、模糊化等等,甚至你将原图片完全替换成另外一张图都是可以的。

但是这里显然我不可能向大家演示所有图片变换的可能,图片变换的可能性也是无限的。因此这里我们就选择一种常用的图片变换效果来进行自定义吧——对图片进行圆形化变换。

图片圆形化的功能现在在手机应用中非常常见,比如手机QQ就会将用户的头像进行圆形化变换,从而使得界面变得更加好看。

自定义图片变换功能的实现逻辑比较固定,我们刚才看过CenterCrop的源码之后,相信你已经基本了解整个自定义的过程了。其实就是自定义一个类让它继承自BitmapTransformation ,然后重写transform()方法,并在这里去实现具体的图片变换逻辑就可以了。一个空的图片变换实现大概如下所示:

public class CircleCrop extends BitmapTransformation {

public CircleCrop(Context context) {

super(context);

}

public CircleCrop(BitmapPool bitmapPool) {

super(bitmapPool);

}

@Override

public String getId() {

return “com.example.glidetest.CircleCrop”;

}

@Override

protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {

return null;

}

}

这里有一点需要注意,就是getId()方法中要求返回一个唯一的字符串来作为id,以和其他的图片变换做区分。通常情况下,我们直接返回当前类的完整类名就可以了。

另外,这里我们选择继承BitmapTransformation还有一个限制,就是只能对静态图进行图片变换。当然,这已经足够覆盖日常95%以上的开发需求了。如果你有特殊的需求要对GIF图进行图片变换,那就得去自己实现Transformation接口才可以了。不过这个就非常复杂了,不在我们今天的讨论范围。

好了,那么我们继续实现对图片进行圆形化变换的功能,接下来只需要在transform()方法中去做具体的逻辑实现就可以了,代码如下所示:

public class CircleCrop extends BitmapTransformation {

public CircleCrop(Context context) {

super(context);

}

public CircleCrop(BitmapPool bitmapPool) {

super(bitmapPool);

}

@Override

public String getId() {

return “com.example.glidetest.CircleCrop”;

}

@Override

protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {

int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());

final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);

final Bitmap result;

if (toReuse != null) {

result = toReuse;

} else {

result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);

}

int dx = (toTransform.getWidth() - diameter) / 2;

int dy = (toTransform.getHeight() - diameter) / 2;

Canvas canvas = new Canvas(result);

Paint paint = new Paint();

BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,

BitmapShader.TileMode.CLAMP);

if (dx != 0 || dy != 0) {

Matrix matrix = new Matrix();

matrix.setTranslate(-dx, -dy);

shader.setLocalMatrix(matrix);

}

paint.setShader(shader);

paint.setAntiAlias(true);

float radius = diameter / 2f;

canvas.drawCircle(radius, radius, radius, paint);

if (toReuse != null && !pool.put(toReuse)) {

toReuse.recycle();

}

return result;

}

}

下面我来对transform()方法中的逻辑做下简单的解释。首先第18行先算出原图宽度和高度中较小的值,因为对图片进行圆形化变换肯定要以较小的那个值作为直径来进行裁剪。第20-26行则和刚才一样,从Bitmap缓存池中尝试获取一个Bitmap对象来进行重用,如果没有可重用的Bitmap对象的话就创建一个。第28-41行是具体进行圆形化变换的部分,这里算出了画布的偏移值,并且根据刚才得到的直径算出半径来进行画圆。最后,尝试将复用的Bitmap对象重新放回到缓存池当中,并将圆形化变换后的Bitmap对象进行返回。

这样,一个自定义图片变换的功能就写好了,那么现在我们就来尝试使用一下它吧。使用方法非常简单,刚才已经介绍过了,就是把这个自定义图片变换的实例传入到transform()方法中即可,如下所示:

Glide.with(this)

.load(url)

.transform(new CircleCrop(this))

.into(imageView);

现在我们重新运行一下程序,效果如下图所示。

更多图片变换功能

========

虽说Glide的图片变换功能框架已经很强大了,使得我们可以轻松地自定义图片变换效果,但是如果每一种图片变换都要我们自己去写还是蛮吃力的。事实上,确实也没有必要完全靠自己去实现各种各样的图片变换效果,因为大多数的图片变换都是比较通用的,各个项目会用到的效果都差不多,我们每一个都自己去重新实现无异于重复造轮子。

也正是因此,网上出现了很多Glide的图片变换开源库,其中做的最出色的应该要数glide-transformations这个库了。它实现了很多通用的图片变换效果,如裁剪变换、颜色变换、模糊变换等等,使得我们可以非常轻松地进行各种各样的图片变换。

glide-transformations的项目主页地址是 https://github.com/wasabeef/glide-transformations

下面我们就来体验一下这个库的强大功能吧。首先需要将这个库引入到我们的项目当中,在app/build.gradle文件当中添加如下依赖:

dependencies {

compile ‘jp.wasabeef:glide-transformations:2.0.2’

}

现在如果我想对图片进行模糊化处理,那么就可以使用glide-transformations库中的BlurTransformation这个类,代码如下所示:

Glide.with(this)

.load(url)

.bitmapTransform(new BlurTransformation(this))

.into(imageView);

注意这里我们调用的是bitmapTransform()方法而不是transform()方法,因为glide-transformations库都是专门针对静态图片变换来进行设计的。现在重新运行一下程度,效果如下图所示。

没错,我们就这样轻松地实现模糊化的效果了。

接下来我们再试一下图片黑白化的效果,使用的是GrayscaleTransformation这个类,代码如下所示:

Glide.with(this)

.load(url)

.bitmapTransform(new GrayscaleTransformation(this))

.into(imageView);

尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

roid开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

[外链图片转存中…(img-zWvVCDX5-1713474993630)]

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-YBvdk8FT-1713474993630)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-vLHUjXFX-1713474993630)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值