一个问题
加载一张图片尺寸为540*258像素的百度logo
现在编写一个布局文件,布局文件中只有一个按钮和一个用于显示图片的ImageView。注意,ImageView的宽和高这里设置的都是wrap_content
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>java
public class MainActivity extends Activity {
ImageView imageView;
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
loadImage();
}
});
}
private void loadImage() {
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是什么
用imageView.getScaleType()方法获取可以获取ImageView的scaleType
Toast.makeText(this,imageView.getScaleType()+"",Toast.LENGTH_SHORT).show();重新运行程序,发现默认是FIT_CENTER

看Glide相关源码
public Target<TranscodeType> 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;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}这里在第7行会进行一个switch判断,如果ImageView的scaleType是CENTER_CROP,则会去调用applyCenterCrop()方法,如果scaleType是FIT_CENTER、FIT_START或FIT_END,则会去调用applyFitCenter()方法。这里的applyCenterCrop()和applyFitCenter()方法其实就是向Glide的加载流程中添加了一个图片变换操作,具体的源码我们就不跟进去看了
由于ImageView默认的scaleType是FIT_CENTER,Glide会自动添加一个FitCenter的图片变换,而在这个图片变换过程中做了某些操作,导致图片充满了全屏
那么我们该如何解决这个问题呢
实际上,Glide给我们提供了专门的API来添加和取消图片变换,想要解决这个问题只需要使用如下代码即可:
Glide.with(this)
.load(url)
.dontTransform()
.into(imageView);这里调用了一个dontTransform()方法,表示让Glide在加载图片的过程中不进行图片变换,这样刚才调用的applyCenterCrop()、applyFitCenter()就统统无效了

但是使用dontTransform()方法存在着一个问题,就是调用这个方法之后,所有的图片变换操作就全部失效了,就连默认的FIT_CENTER都没有效果了。那如果我有一些图片变换操作是必须要执行的该怎么办呢?不用担心,总归是有办法的,这种情况下我们只需要借助override()方法强制将图片尺寸指定成原始大小就可以了,代码如下:
Glide.with(this)
.load(url)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(imageView);通过override()方法将图片的宽和高都指定成Target.SIZE_ORIGINAL,问题同样被解决了。程序的最终运行结果和上图是完全一样的,我就不再重新截图了
图片变换的基本用法
图片变换的意思就是说,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()方法来实现的
FitCenter的效果其实刚才我们已经见识过了,就是会将图片按照原始的长宽比充满全屏
CenterCrop又是什么样的效果呢?我们来动手试一下
这是不使用任何变换的情况下,图片效果

这是FIT_CENTER的效果

现在我们添加一个CenterCrop的图片变换操作:
String url = "http://baike.ucwz.net/uploads/201503/1427206389L1T22f7b.jpg";
Glide.with(this)
.load(url)
.centerCrop()
.into(imageView);
现在展示的图片是对原图的中心区域进行裁剪后得到的图片
centerCrop()方法还可以配合override()方法来实现更加丰富的效果,比如指定图片裁剪的比例:
String url = "http://baike.ucwz.net/uploads/201503/1427206389L1T22f7b.jpg";
Glide.with(this)
.load(url)
.override(500, 500)
.centerCrop()
.into(imageView);这里我们将图片的尺寸设定为500*500像素,那么裁剪的比例也就变成1:1了

自定义图片变换
Glide给我们定制好了一个图片变换的框架,大致的流程是我们可以获取到原始的图片,然后对图片进行变换,再将变换完成后的图片返回给Glide,最终由Glide将图片显示出来。理论上,在对图片进行变换这个步骤中我们可以进行任何的操作,你想对图片怎么样都可以。包括圆角化、圆形化、黑白化、模糊化等等,甚至你将原图片完全替换成另外一张图都是可以的
我们来做一个圆角化图片的变换
自定义图片变换功能的实现逻辑比较固定,就是自定义一个类让它继承自BitmapTransformation ,然后重写transform()方法,并在这里去实现具体的图片变换逻辑就可以了。一个空的图片变换实现大概如下所示:
public class CircleCrop extends BitmapTransformation{
public CircleCrop(Context context) {
super(context);
}
public CircleCrop(BitmapPool bitmapPool) {
super(bitmapPool);
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return null;
}
@Override
public String getId() {
return “com.xy.test.CircleCrop”;
}
}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;
}
}首先第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
郭大神原文地址