由于本人之前并未接触过图片的加载框架,近期在开发过程中频繁遇到了Glide的代码,因而对其的基本用法做了简单的学习,本次对学习中遇到的要点做一个简单的归纳总结。
Why Glide?
Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片。
相比其他框架,Glide有以下优势:
- 易用:通过简单明了的链式编程,即可轻松实现图片加载
- 不用担心内存浪费:Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支
- 支持加载gif图片
如何使用Glide?
前期工作
构建配置
在正式使用之前,我们首先要对Glide进行配置。在build.gradle中添加以下代码:
repositories {
mavenCentral()
maven { url 'https://maven.google.com' }
}
dependencies {
compile 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
权限设置
根据不同的使用场景,Glide可能需要以下几条权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
其他设置
Proguard
如果你有使用到 proguard,那么请把以下代码添加到你的 proguard.cfg 文件中:
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
如果你使用 DexGuard 你可能还需要添加:
# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
开始使用
大多数情况下,Glide加载图片都只需要一行代码:
Glide.with(fragment)
.load(myUrl)
.into(imageView);
取消加载同样很简单:
Glide.with(fragment).clear(imageView);
其实,取消加载的代码不是必须的,当with()中传入的Activity或Fragment实例销毁时,Glide会自动取消加载并回收资源。当然,如果我们传入的是Application,那么Glide将会在应用关闭之时自动取消加载。
下面让我们简单实践一下,打开AS,建立一个名为GlideTest的Empty Activity项目,将自动生成的布局文件中的代码修改:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:text="Hello World!"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
接着,修改MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
String url = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2F80%2Fv2-d4932fcb856650c5ef93bd3a69704afa_720w.jpg%3Fsource%3D1940ef5c&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639115414&t=3bb39f0df351ff0c7fb3cd3e1a8e3cd9";
ImageView imageView;
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.img);
button = findViewById(R.id.btn);
button.setOnClickListener(v -> Glide.with(this)
.load(url)
.into(imageView));
}
}
运行程序,点击按钮,效果如下图:
分析
with()
它可以接受Context、Activity或者Fragment类型的参数,从而决定Glide加载图片的生命周期。
load()
这个方法用于指定待加载的图片资源。Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。因此load()方法也有很多个方法重载,除了我们刚才使用的加载一个字符串网址之外,你还可以这样使用load()方法:
// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);
// 加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);
// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);
// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
into()
该方法很简单,它接收显示我们图片的容器。
占位图
观察刚才的显示效果,你会发现图片显示之前有一段延迟,如果网络条件比较差,延迟也将会随之增大。而占位图就是指在图片的加载过程中,我们先显示一张临时的图片,等图片加载出来了再替换成要加载的图片。
Glide允许用户指定三种不同类型的占位符,分别在三种不同场景使用:
- place holder
- error
- fallback
占位符(Placeholder)
占位符是当请求正在执行时被展示的 Drawable 。当请求成功完成时,占位符会被请求到的资源替换。如果被请求的资源是从内存中加载出来的,那么占位符可能根本不会被显示。如果请求失败并且没有设置 error Drawable ,则占位符将被持续展示。类似地,如果请求的url/model为 null ,并且 error Drawable 和 fallback 都没有设置,那么占位符也会继续显示。
Glide.with(fragment)
.load(url)
.placeholder(R.drawable.placeholder)
.into(view);
Or:
Glide.with(fragment)
.load(url)
.placeholder(new ColorDrawable(Color.BLACK))
.into(view);
错误符(Error)
error Drawable 在请求永久性失败时展示。error Drawable 同样也在请求的url/model为 null ,且并没有设置 fallback Drawable 时展示。
Glide.with(fragment)
.load(url)
.error(R.drawable.error)
.into(view);
Or:
Glide.with(fragment)
.load(url)
.error(new ColorDrawable(Color.RED))
.into(view);
后备回调符(Fallback)
fallback Drawable 在请求的url/model为 null 时展示。设计 fallback Drawable 的主要目的是允许用户指示 null 是否为可接受的正常情况。例如,一个 null 的个人资料 url 可能暗示这个用户没有设置头像,因此应该使用默认头像。然而,null 也可能表明这个元数据根本就是不合法的,或者取不到。 默认情况下Glide将 null 作为错误处理,所以可以接受 null 的应用应当显式地设置一个 fallback Drawable 。
Glide.with(fragment)
.load(url)
.fallback(R.drawable.fallback)
.into(view);
Or:
Glide.with(fragment)
.load(url)
.fallback(new ColorDrawable(Color.GREY))
.into(view);
实践
这里我们以占位符为例,动手操作一下。
首先我事先准备好了一张loading.jpg图片,用来作为占位图显示。然后修改Glide加载部分的代码,如下所示:
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.into(imageView);
这个占位图的用法其实也演示了Glide当中绝大多数API的用法,其实就是在load()和into()方法之间串接任意想添加的功能。
不过如果你现在重新运行一下代码并点击Load Image,很可能是根本看不到占位图效果的。因为Glide有非常强大的缓存机制,我们刚才加载那张必应美图的时候Glide自动就已经将它缓存下来了,下次加载的时候将会直接从缓存中读取,不会再去网络下载了,因而加载的速度非常快,所以占位图可能根本来不及显示。
因此这里我们还需要稍微做一点修改,来让占位图能有机会显示出来,修改代码如下所示:
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
可以看到,这里串接了一个diskCacheStrategy()方法,并传入DiskCacheStrategy.NONE参数,这样就可以禁用掉Glide的缓存功能。
重新运行代码,我们就可以看到当点击Load Image按钮之后会立即显示一张占位图,然后等真正的图片加载完成之后会将占位图替换掉。
指定文件格式
我们知道,Glide是支持加载GIF图片的。而且,而使用Glide加载GIF图并不需要编写什么额外的代码,只要传入一个GIF图片,Glide就会自动判断图片格式,并正确的将其解析出来。
如果我们想指定图片格式,比如就想加载一张静态图片,该怎么办呢?
想实现这个功能仍然非常简单,我们只需要再串接一个新的方法就可以了,如下所示:
Glide.with(this)
.load(url)
.asBitmap()
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
这里在load()方法的后面加入了一个asBitmap()方法,这个方法的意思就是说这里只允许加载静态图片,不需要Glide去帮我们自动进行图片格式的判断了。
如果我们反其道而行之,也就是将一个静态图片当做gif图加载,情况会如何呢?将asGif()方法替代了asBitmap()方法,并传入一张静态图片,重新运行,结果当然是加载失败。
指定图片大小
在绝大多数情况下我们都是不需要指定图片大小的,因为Glide会自动根据ImageView的大小来决定图片的大小。
不过,如果你真的有这样的需求,必须给图片指定一个固定的大小,Glide仍然是支持这个功能的。修改Glide加载部分的代码,如下所示:
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)
.into(imageView);
仍然非常简单,这里使用override()方法指定了一个图片的尺寸,也就是说,Glide现在只会将图片加载成100*100像素的尺寸,而不会管你的ImageView的大小是多少了。
注意
- 对url进行Null校验并不是必须的,当url为null时,Glide会情况view的内容,或显示placeholder、fallback的内容
- 如果一个可复用的view先前用glide加载过图片,在新的加载操作(如:setImaDrawable())之前,需调用clear取消加载,否则Glide可能将view改回原来的内容
- 占位符是在主线程从Android Resources加载的。我们通常希望占位符比较小且容易被系统资源缓存机制缓存起来。
- Transformation仅被应用于被请求的资源,而不会对任何占位符使用。在应用中包含必须在运行时做变换才能使用的图片资源是很不划算的。相反,在应用中包含一个确切符合尺寸和形状要求的资源版本几乎总是一个更好的办法。假如你正在加载圆形图片,你可能希望在你的应用中包含圆形的占位符。另外你也可以考虑自定义一个View来剪裁(clip)你的占位符,而达到你想要的变换效果