=====
Android图片加载框架最全解析(五),Glide强大的图片变换功能
Glide的这个框架的功能实在是太强大了,它所能做的事情远远不止于目前我们所学的这些。因此,今天我们就再来学习一个新的功能模块,并且是一个非常重要的模块——Glide的图片变化功能。
一个问题
====
在正式开始学习Glide的图片变化功能之前,我们先来看一个问题,这个问题可能有不少人都在使用Glide的时候都遇到过,正好在本篇内容的主题之下我们顺带着将这个问题给解决了。
首先我们尝试使用Glide来加载一张图片,图片URL地址是:
https://www.baidu.com/img/bd_logo1.png
这是百度首页logo的一张图片,图片尺寸是540*258像素。
接下来我们编写一个非常简单的布局文件,如下所示:
<LinearLayout
xmlns:android=“http://schemas.android.com/apk/res/android”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:orientation=“vertical”>
<Button
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“Load Image”
android:onClick=“loadImage”
/>
<ImageView
android:id=“@+id/image_view”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
/>
布局文件中只有一个按钮和一个用于显示图片的ImageView。注意,ImageView的宽和高这里设置的都是wrap_content。
然后编写如下的代码来加载图片:
public class MainActivity extends AppCompatActivity {
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
}
public void loadImage(View view) {
String url = “https://www.baidu.com/img/bd_logo1.png”;
Glide.with(this)
.load(url)
.into(imageView);
}
}
这些简单的代码对于现在的你而言应该都是小儿科了,相信我也不用再做什么解释。现在运行一下程序并点击加载图片按钮,效果如下图所示。
图片是正常加载出来了,不过大家有没有发现一个问题。百度这张logo图片的尺寸只有540*258像素,但是我的手机的分辨率却是1080*1920像素,而我们将ImageView的宽高设置的都是wrap_content,那么图片的宽度应该只有手机屏幕宽度的一半而已,但是这里却充满了全屏,这是为什么呢?
如果你之前也被这个问题困扰过,那么恭喜,本篇文章正是你所需要的。之所以会出现这个现象,就是因为Glide的图片变换功能所导致的。那么接下来我们会先分析如何解决这个问题,然后再深入学习Glide图片变化的更多功能。
稍微对Android有点了解的人应该都知道ImageView有scaleType这个属性,但是可能大多数人却不知道,如果在没有指定scaleType属性的情况下,ImageView默认的scaleType是什么?
这个问题如果直接问我,我也答不上来。不过动手才是检验真理的唯一标准,想知道答案,自己动手试一下就知道了。
public class MainActivity extends AppCompatActivity {
private static final String TAG = “MainActivity”;
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
Log.d(TAG, "imageView scaleType is " + imageView.getScaleType());
}
…
}
可以看到,我们在onCreate()方法中打印了ImageView默认的scaleType,然后重新运行一下程序,结果如下图所示:
由此我们可以得知,在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER。
有了这个前提条件,我们就可以继续去分析Glide的源码了。
回顾一下我们分析过的into()方法,它是在GenericRequestBuilder类当中的,代码如下所示:
public Target into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException(“You must pass in a non null View”);
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
// C A S E S − O M I T T E D CASES-OMITTED CASES−OMITTED
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
还记得我们当初分析这段代码的时候,直接跳过前面的所有代码,直奔最后一行。因为那个时候我们的主要任务是分析Glide的主线执行流程,而不去仔细阅读它的细节,但是现在我们是时候应该阅读一下细节了。
可以看到,这里在第7行会进行一个switch判断,如果ImageView的scaleType是CENTER_CROP,则会去调用applyCenterCrop()方法,如果scaleType是FIT_CENTER、FIT_START或FIT_END,则会去调用applyFitCenter()方法。这里的applyCenterCrop()和applyFitCenter()方法其实就是向Glide的加载流程中添加了一个图片变换操作,具体的源码我们就不跟进去看了。
那么现在我们就基本清楚了,由于ImageView默认的scaleType是FIT_CENTER,因此会自动添加一个FitCenter的图片变换,而在这个图片变换过程中做了某些操作,导致图片充满了全屏。
那么我们该如何解决这个问题呢?最直白的一种办法就是看着源码来改。当ImageView的scaleType是CENTER_CROP、FIT_CENTER、FIT_START或FIT_END时不是会自动添加一个图片变换操作吗?那我们把scaleType改成其他值不就可以了。ImageView的scaleType可选值还有CENTER、CENTER_INSIDE、FIT_XY等。这当然是一种解决方案,不过只能说是一种比较笨的解决方案,因为我们为了解决这个问题而去改动了ImageView原有的scaleType,那如果你真的需要ImageView的scaleType为CENTER_CROP或FIT_CENTER时可能就傻眼了。
上面只是我们通过分析源码得到的一种解决方案,并不推荐大家使用。实际上,Glide给我们提供了专门的API来添加和取消图片变换,想要解决这个问题只需要使用如下代码即可:
Glide.with(this)
.load(url)
.dontTransform()
.into(imageView);
可以看到,这里调用了一个dontTransform()方法,表示让Glide在加载图片的过程中不进行图片变换,这样刚才调用的applyCenterCrop()、applyFitCenter()就统统无效了。
现在我们重新运行一下代码,效果如下图所示:
这样图片就只会占据半个屏幕的宽度了,说明我们的代码奏效了。
但是使用dontTransform()方法存在着一个问题,就是调用这个方法之后,所有的图片变换操作就全部失效了,那如果我有一些图片变换操作是必须要执行的该怎么办呢?不用担心,总归是有办法的,这种情况下我们只需要借助override()方法强制将图片尺寸指定成原始大小就可以了,代码如下所示:
Glide.with(this)
.load(url)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(imageView);
通过override()方法将图片的宽和高都指定成Target.SIZE_ORIGINAL,问题同样被解决了。程序的最终运行结果和上图是完全一样的,我就不再重新截图了。
由此我们可以看出,之所以会出现这个问题,和Glide的图片变换功能是撇不开关系的。那么也是通过这个问题,我们对Glide的图片变换有了一个最基本的认识。接下来,就让我们正式开始进入本篇文章的正题吧。
图片变换的基本用法
=========
顾名思义,图片变换的意思就是说,Glide从加载了原始图片到最终展示给用户之前,又进行了一些变换处理,从而能够实现一些更加丰富的图片效果,如图片圆角化、圆形化、模糊化等等。
添加图片变换的用法非常简单,我们只需要调用transform()方法,并将想要执行的图片变换操作作为参数传入transform()方法即可,如下所示:
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 {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。
在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
给大家展示一部分。
[外链图片转存中…(img-2DcfZoGR-1712136385089)]
[外链图片转存中…(img-wRmZC8MP-1712136385090)]
[外链图片转存中…(img-RaKpU1bs-1712136385090)]
还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算