GlideV4使用详解

概述

要想使用Glidev4版本,首先需要将这个库引入到我们的项目当中。在你的 build.gradle 中添加下面这行代码:

implementation 'com.github.bumptech.glide:glide:4.9.0'
//使用Generated API需要引入 
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

目前最新的版本就是 4.9.0 这个版本。

注意:Glide中需要用到网络功能,因此你还得在 AndroidManifest.xml 中声明一下网络权限才行:

<uses-permission android:name="android.permission.INTERNET"/>

如果权限没加,虽然不会报错,但是图片加载没有反应。

如果你正在从 URL 加载图片,Glide 可以自动帮助你处理片状网络连接:它可以监听用户的连接状态并在用户重新连接到网络时重启之前失败的请求。如果 Glide 检测到你的应用拥有 ACCESS_NETWORK_STATE 权限,Glide 将自动监听连接状态而不需要额外的改动。所以我们最好还要把 ACCESS_NETWORK_STATE 权限也加上。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

如果要从本地文件夹加载图片或缓存图片到本地,则还需要读写权限,如下:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

加载图片

如果你想从网络上加载一张图片到 ImageView 上,只需要这样写:

String url = "http://www.jianbihua.cc/uploads/allimg/140215/2-140215123319130.jpg";

Glide.with(this).load(url).into(iv_image);

运行效果如下:

一个完整的功能请求,要求至少有三个参数,先 with() ,再 load() ,最后 into()

  • with() ,用于创建一个加载图片的实例。 with() 方法可以接收 Context、Activity或者 Fragment 类型的参数。不管是在 Activity 还是 Fragment中 调用with()方法,都可以直接传 this 。那如果调用的地方既不在 Activity 中也不在 Fragment 中呢?也没关系,我们可以获取当前应用程序的ApplicationContext,传入到with()方法当中。注意 with() 方法中传入的实例会决定 Glide 加载图片的生命周期,如果传入的是Activity或者 Fragment 的实例,那么当这个 ActivityFragment 被销毁的时候,Glide 会自动取消加载并回收资源。如果传入的是 ApplicationContext ,那么只有当应用程序被杀掉的时候,图片加载才会停止。

  • load() ,这个方法用于指定待加载的图片资源,Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。

  • into() ,需要加载显示图片的 ImageView ,也有一些重载的方法,属于高级用法,后面再讲。

除外,我们也可以调用对应的 clear 方法来手动取消加载:

Glide.with(this).clear(imageView);

加载进阶

三步走之中,估计大家最关心的就是 load() 方法,那么我们再进阶一下,看看有哪些重载方法可以用。

从应用资源中加载

int resourceId = R.mipmap.ic_launcher;

Glide.with(context).load(resourceId).into(imageViewResource);

从文件中加载

//这个文件可能不存在于你的设备中。然而你可以指定一个图片路径。
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Running.jpg");

Glide.with(context).load(file).into(imageViewFile);

从 Uri 中加载

Uri uri = resourceIdToUri(context, R.mipmap.future_studio_launcher);

Glide.with(context).load(uri).into(imageViewUri);

占位图

Glide允许用户指定三种不同类型的占位符,分别在三种不同场景使用:

占位符(Placeholder):当请求正在执行时被展示的 Drawable ,当请求成功完成时,占位符会被请求到的资源替换。如果被请求的资源是从内存中加载出来的,那么占位符可能根本不会被显示。如果请求失败并且没有设置 error Drawable ,则占位符将被持续展示。类似地,如果请求的url/model为 null ,并且 error Drawable 和 fallback 都没有设置,那么占位符也会继续显示。

错误符(Error): 请求失败后被展示的 Drawable ,如果请求的url/model为 null ,并且 fallback 没有设置,那么该占位符也会继续显示。

后备回调符(Fallback): 在请求的url/model为 null 时展示,对 null 的一个特殊处理。

总结:如果只设置了 Placeholder ,则出现 error 或 url 为 null 时,还会展示 Placeholder 。如果也设置了 error ,则出现 error 或 url 为 null 时,会展示 error 。如果也设置了 fallback ,则会在 url 为 null 时显示 fallback 。

Glide.with(this).load(url).placeholder(new ColorDrawable(Color.BLACK))
        .error(new ColorDrawable(Color.RED))
        .fallback(new ColorDrawable(Color.GRAY)).into(mImageView);

Glide.with(this).load(url).placeholder(R.mipmap.icon_place_holder)
        .error(R.mipmap.icon_error)
        .fallback(R.mipmap.icon_fallbackl).into(mImageView);

对应的占位符可以传入 Drawable 或资源 ID 。

在失败时开始新的请求

使用 error API 来指定一个 RequestBuilder,以在主请求失败时开始一次新的加载,如下示例会在 url2 加载失败时加载 url 。

Glide.with(this).load(url2).placeholder(R.mipmap.icon_place_holder)
        .error(Glide.with(this).load(url))
        .fallback(R.mipmap.icon_fallbackl).into(mImageView2);

选项(Options)

请求选项(RequestOptions)

Glide中的大部分设置项都可以直接应用在 Glide.with().load(url) 返回的 RequestBuilder 对象上。如果你想让你的应用的不同部分之间共享相同的加载选项,你也可以初始化一个新的 RequestOptions 对象,并在每次加载时通过 apply() 方法传入这个对象:

RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(new ColorDrawable(Color.BLACK))
        .error(new ColorDrawable(Color.RED))
        .fallback(new ColorDrawable(Color.GRAY));

Glide.with(this).load(url).apply(requestOptions).into(mImageView);

Glide.with(this).load(url2).apply(requestOptions).into(mImageView2);

apply() 方法可以被调用多次,因此 RequestOption 可以被组合使用。如果 RequestOptions 对象之间存在相互冲突的设置,那么只有最后一个被应用的 RequestOptions 会生效。

过渡选项(TransitionOptions )

TransitionOptions 用于决定你的加载完成时会发生什么,使用 TransitionOption 可以应用以下变换:

  • View淡入
  • 与占位符交叉淡入
  • 或者什么都不发生

不同于RequestOptions,TransitionOptions是特定资源类型独有的,你能使用的变换取决于你让Glide加载哪种类型的资源。这样的结果是,假如你请求加载一个 Bitmap ,你需要使用 BitmapTransitionOptions ,而不是 DrawableTransitionOptions 。

Glide.with(this).load(url).transition(DrawableTransitionOptions
        .withCrossFade(new DrawableCrossFadeFactory.Builder(1000).setCrossFadeEnabled(true).build()))
        .skipMemoryCache(true)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .apply(requestOptions).into(mImageView);

Glide.with(this).asBitmap().load(url2).transition(BitmapTransitionOptions.withCrossFade(1000)).apply(requestOptions).into(mImageView2);

RequestBuilder

RequestBuilder 是Glide中请求的骨架,负责携带请求的url和你的设置项来开始一个新的加载过程。RequestBuilders 是特定于它们将要加载的资源类型的,默认情况下你会得到一个 Drawable RequestBuilder ,要构造一个 RequestBuilder 对象,你可以通过先调用 Glide.with()然后再调用某一个 as 方法来完成:

RequestBuilder<Bitmap> requestBuilder2 = Glide.with(this).asBitmap(); 

或先调用 Glide.with() 然后 load():

RequestBuilder<Drawable> requestBuilder = Glide.with(this).load(url);

RequestBuilder 也可以被复用于开始多个请求:

RequestBuilder<Drawable> requestBuilder =
        Glide.with(fragment)
            .asDrawable()
            .apply(requestOptions);

for (int i = 0; i < numViews; i++) {
   ImageView view = viewGroup.getChildAt(i);
   String url = urls.get(i);
   requestBuilder.load(url).into(view);
}

加载动画

我们在加载图片的时候,肯定不希望图片突然显示出来,平滑地显示是非常有必要的,Glide 默认开启淡入淡出动画,我们在代码中不设置,默认开启,设置了也可以,代码如下:

String url = "http://www.jianbihua.cc/uploads/allimg/140215/2-140215123319130";
Glide.with(this)
.load(url)
.placeholder(R.mipmap.icon_home_placeholder)
.error(R.mipmap.icon_loading_error)
.crossFade()
.into(iv_image);

crossFade() 方法还有一个重载方法 crossFade(int duration) ,duration 用来设置动画持续时间,默认时间是300毫秒。具体的效果,大家可以自行测试观察。

可能这时候就有小伙伴要搞事情了,我就不想要加载动画,直接显示图片,那也是可以的。 .dontAnimate() 方法即可禁掉加载动画,代码如下:

String url = "http://www.jianbihua.cc/uploads/allimg/140215/2-140215123319130.jpg";
Glide.with(this).load(url).dontAnimate().into(iv_image);

可能这时候又有小伙伴要搞事情了,我要动画,但是我想自定义加载动画,不要默认的,那也是可以的。在讲自定义动画前,我要说明一下,动画仅仅用于不从缓存中加载的情况。如果图片被缓存过了,它的显示是非常快的,因此动画是没有必要的,并且不显示的。

动画设置通过方法 animate(),该方法有两个重载方法,也就代码着两种不同的加载动画方式。

从资源中加载动画

我们可以传入一个动画id来加载图片动画,比如我们设置从左滑入的动画, android.R.anim.slide_in_left 。下面这段代码是这个动画的XML描述:

<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android">  
    <translate android:fromXDelta="-50%p" android:toXDelta="0"
        android:duration="@android:integer/config_mediumAnimTime"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
        android:duration="@android:integer/config_mediumAnimTime" />
</set> 

从左滑入的代码设置如下:

String url = "http://www.jianbihua.cc/uploads/allimg/140215/2-140215123319130.jpg";

Glide.with(this).load(url).animat(android.R.anim.slide_in_left).into(iv_image);

上述的代码是Android自带的动画,我们可以创建自己的XML动画,比如一个先放大后缩小到原来大小的动画。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true" android:duration="1000">

<scale
    android:fromXScale="1.2"
    android:fromYScale="1.2"
    android:toXScale="1.0"
    android:toYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%" />

</set>

缩放动画代码设置如下:

Glide.with(this).load(url).animat(R.anim.zoom_in).into(iv_image);

通过自定义类实现动画

我们通过这种方式来实现一下上述的缩放动画:

ViewPropertyAnimation.Animator animatorObject = new ViewPropertyAnimation.Animator() {
        @Override
        public void animate(View view) {

            //  如果 view 是一个自定义的View ,可以在这先找到要实现动画的子View,再做动画处理。

            ObjectAnimator animatorX = ObjectAnimator.ofFloat(view, "scaleX", 1.2f, 1.0f);
            ObjectAnimator animatorY = ObjectAnimator.ofFloat(view, "scaleY", 1.2f, 1.0f);
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.play(animatorX).with(animatorY);
            animatorSet.setDuration(2000);
            animatorSet.start();
        }
    };

Glide.with(this).load(url).animat(animatorObject).into(iv_image);

最后实现的效果和上述的一样。这种方式可以实现一些比较复杂的动画,也可以解决 into() 方法中没有加载常规ImageView,而是其他对象时的动画处理。我们在后面再讲 into() 加载的其他对象。

指定图片大小

实际上,使用Glide在绝大多数情况下我们都是不需要指定图片大小的。

在学习本节内容之前,你可能还需要先了解一个概念,就是我们平时在加载图片的时候很容易会造成内存浪费。什么叫内存浪费呢?比如说一张图片的尺寸是10001000像素,但是我们界面上的ImageView可能只有200200像素,这个时候如果你不对图片进行任何压缩就直接读取到内存中,这就属于内存浪费了,因为程序中根本就用不到这么高像素的图片。

而使用Glide,我们就完全不用担心图片内存浪费,甚至是内存溢出的问题。因为Glide从来都不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少。Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。

也正是因为Glide是如此的智能,所以刚才在开始的时候我就说了,在绝大多数情况下我们都是不需要指定图片大小的,因为Glide会自动根据ImageView的大小来决定图片的大小。

当你还没有目标 View 去知道尺寸的时候,比如,如果 APP 想要在闪屏界面预热缓存,它还不能测量 ImageView 的尺寸。然而,你如果知道这个图片要多小,用 override(width,height) 提供明确的尺寸。

Glide.with(this).load(url).override(200,200).into(iv_image);

现在 Glide只会将图片加载成200*200像素的尺寸,而不会管你的 ImageView的大小是多少了。

变换图像

在Glide中,Transformations 可以获取资源并修改它,然后返回被修改后的资源。通常变换操作是用来完成剪裁或对位图应用过滤器,但它也可以用于转换GIF动画,甚至自定义的资源类型。 默认变换是 fitCenter ,Glide 提供了三个标准选项: centerCrop()circleCropfitCenter() 。其实效果跟设置 ImageView 的 ScaleType 是一样的。

CenterCrop() ,即缩放图像让它填充到 ImageView 界限内并且裁剪额外的部分。ImageView 可能会完全填充,但图像可能不会完整显示。

fitCenter() ,即缩放图像让图像都测量出来等于或小于 ImageView 的边界范围。该图像将会完全显示,但可能不会填满整个 ImageView。

circleCrop() ,变换为圆形。

代码和效果如下:

// 通过 RequestOptions 也可以设置
RequestOptions options = new RequestOptions().centerCrop();

Glide.with(this).load(url).centerCrop().into(iv_image);

默认情况下,每个 transform() 调用,或任何特定转换方法(fitCenter(), centerCrop(), bitmapTransform() etc)的调用都会替换掉之前的变换。

如果你想在单次加载中应用多个变换,可使用 .transform() ,传入多个变换类。

Glide.with(this).load(url).transform(new CenterCrop(),new CircleCrop()).placeholder(new ColorDrawable(Color.BLACK))
        .error(new ColorDrawable(Color.RED))
        .fallback(new ColorDrawable(Color.GRAY)).into(mImageView);

注意:传入变换参数的顺序,决定了这些变换的应用顺序。Transformation 可以被重复使用

ImageView的自动变换

在Glide中,当你为一个 ImageView 开始加载时,Glide可能会自动应用 FitCenter 或 CenterCrop ,这取决于view的 ScaleType 。如果 scaleType 是 CENTER_CROP , Glide 将会自动应用 CenterCrop 变换。如果 scaleType 为 FIT_CENTER 或 CENTER_INSIDE ,Glide会自动使用 FitCenter 变换。当然,可以使用 transform() 方法来传入我们想要的变换。另外,也可以通过使用 dontTransform() 确保不会自动应用任何变换。

指定图片格式

Glide是支持加载 GIF 图片的,而使用Glide加载GIF图并不需要编写什么额外的代码,Glide内部会自动判断图片格式。比如这是一张GIF图片的URL地址:

http://img.zcool.cn/community/01822155c1a44f6ac7253f360673f6.gif

代码如下:

Glide.with(this).load(gifUrl)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(iv_image);

我们只需要像之前那样把地址放在 load() 方法里面即可,如果是 gif ,就会播放。如果加载速度过慢或加载不出来,可以加上 .diskCacheStrategy(DiskCacheStrategy.SOURCE) ,这是 Glide 的缓存策略,后面再讲。

效果如下:

如果你只想显示 GIF 图片的第一帧,可以调用 asBitmap()方法。

Glide.with(this).load(gifUrl)
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(iv_image);

效果如下:

如果你期望这个 URL 是一个 Gif,Glide 不会自动检查是否是 Gif。但是我们可以设置只显示 GIF,如果不是则加载错误,方法为 asGif()

代码如下:

Glide.with(this).load(jpgUrl)
.asGif()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(iv_image);

效果如下:

没错,跟之前的占位图效果一样,即使是正常的图片链接,但是我们显式设置了要显示 GIF ,那只能是加载失败了。

缓存基础

Glide 通过使用默认的内存和硬盘缓存去避免不必要的网络请求。这两个缓存模块的作用各不相同,内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。

你也可以改变 Glide 的默认缓存行为,我们可以调用 .skipMemoryCache(true) 跳过内存缓存。这样 Glide 将不会把这张图片缓存到内存中去了。

注意:对于相同的URL,如果一开始没有跳过内存缓存,后面再调用的话,也还是会从内存中获取缓存的。

对于硬盘缓存,可以用 .diskCacheStrategy() 方法改变默认行为,该方法需要传入一个枚举而不是一个简单的布尔值 。如果你想禁用硬盘缓存,则使用 DiskCacheStrategy.NONE 作为参数。

Glide.with(this).load(url)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(iv_image);

在了解其他枚举值的时候,我们先了解一下 Glide 的缓存策略。 Glide 默认会缓存原始图片和小版本的图片。比如,你请求的图片是 10001000 像素的,但是你的 ImageView 是 500500 像素的,Glide将会把这两个尺寸都进行缓存。下面对枚举参数进行说明:

  • DiskCacheStrategy.NONE 什么都不缓存,就像刚讨论的那样
  • DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像。在我们上面的例子中,将会只有一个 1000x1000 像素的图片
  • DiskCacheStrategy.RESULT 仅仅缓存最终的图像,即,降低分辨率后的(或者是转换后的)
  • DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为)

注意:某些情形下,只加载缓存中有的图片,不在缓存中则加载直接失败(比如省流量模式?),可以使用 onlyRetrieveFromCache 方法。

Glide.with(this).load(url).onlyRetrieveFromCache(true).into(mImageView2);

请求优化级

假设一下有这样一个场景,你需要显示一组图片,中间有一张非常大的,底部有两张小的。怎样才体验友好呢?最好是优先加载中间大图,再显示底部小图了,这就涉及优先级的问题了。 Glide 可以用 priority 枚举来设置优先级,调用 .priority() 方法。

priority 枚举的值如下:

  • Priority.LOW
  • Priority.NORMAL
  • Priority.HIGH
  • Priority.IMMEDIATE

注意:虽然设置了优先级,但是不一定是按优先级的顺序显示图片,但是会尽可能地做优先级的处理显示。

Glide.with(this).load(url).priority(Priority.HIGH).into(iv_image);

缩略图

缩略图是动态占位符。它也可以从网络中加载。如果缩略图比全尺寸图先加载完,就显示缩略图,否则就不显示。方法为 .thumbnail(float sizeMultiplier) ,sizeMultiplier 范围从 0~1f ,如果你传了 0.1f 作为参数, Glide将会显示原始图像的10%的大小。

Glide.with(this).load(url2).thumbnail(Glide.with(this).load(url2).override(mImageView.getWidth(),mImageView.getHeight())).skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE).into(mImageView);

如果你想加载网络图片来做缩略图,可以使用令一个重载方法,代码如下:

Glide.with(this).load(url2).thumbnail(Glide.with(this).load(url)).into(mImageView);

Targets

有时候我们加载图片,不一定要显示到 ImageView 中的,我们只想获取 Bitmap 图片。 Targets 是在做完所有加载和处理之后返回的结果。Targets 有很多种类,下面我们将一一介绍一下:

CustomTarget

默认如果不传入宽高,则图片的尺寸为 Target.SIZE_ORIGINAL,使用 Target.SIZE_ORIGINAL 可能非常低效,或如果你的图片足够大可能引发 OOM 。

Target<Drawable> customTarget = Glide.with(this).load(url)
          .into(new CustomTarget<Drawable>() {
              @Override
              public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
                  mImageView.setImageDrawable(resource);
              }

              @Override
              public void onLoadCleared(@Nullable Drawable placeholder) {

              }
          });

其实我们拿到了 Drawable 或 Bitmap 后可以做很多处理,这里我直接加载到 ImageView ,效果跟之前一样。

之前我们说过,Glide 将自动和我们加载 ImageView宽高尺寸的图片,这样更加高效。但是当我们传入 Target 的时候, Glide将无法知道宽高是多少了,只能加载源图,其实我们是可以自定义 Target 的大小的,下面请看:

Target<Drawable> customTarget = Glide.with(this).load(url)
          .into(new CustomTarget<Drawable>(mImageView.getWidth(), mImageView.getHeight()) {
              @Override
              public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
                  mImageView.setImageDrawable(resource);
              }

              @Override
              public void onLoadCleared(@Nullable Drawable placeholder) {

              }
          });

在之前代码的基础上,我们指定了宽高,这样我们的加载效率会大大提高,如果源图尺寸很大的时候。

你也可以使用返回的 Target 来 clear() 之前的加载,这将在不需要开始新的加载的情况下释放掉任何相关资源(ImageViewTarget才会生效):

Glide.with(this).clear(mImageViewTarget);

ViewTarget

如果你自定义了一个 View ,我们能不能直接通过 into()来展示呢? 答案是肯定的,我们可以通过 ViewTarget 来实现。让我们看一个简单的自定义 View,它继承自 FrameLayout 并内部使用了一个 ImageView 以及覆盖了一个 TextView 。

public class FutureStudioView extends FrameLayout {  
    ImageView iv;
    TextView tv;

    public void initialize(Context context) {
        inflate( context, R.layout.custom_view_futurestudio, this );

        iv = (ImageView) findViewById( R.id.custom_view_image );
        tv = (TextView) findViewById( R.id.custom_view_text );
    }

    public FutureStudioView(Context context, AttributeSet attrs) {
        super( context, attrs );
        initialize( context );
    }

    public FutureStudioView(Context context, AttributeSet attrs, int defStyleAttr) {
        super( context, attrs, defStyleAttr );
        initialize( context );
    }

    public void setImage(Drawable drawable) {
        iv = (ImageView) findViewById( R.id.custom_view_image );

        iv.setImageDrawable( drawable );
    }
}

你不能使用常规的 Glide 的方法 .into(),因为我们的自定义 view 并不继承自 ImageView。因此,我们必须创建一个 ViewTarget,并用 .into() 方法:

private void loadImageViewTarget() {  
    FutureStudioView customView = (FutureStudioView) findViewById( R.id.custom_view );

    viewTarget = new ViewTarget<FutureStudioView, GlideDrawable>( customView ) {
        @Override
        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
            this.view.setImage( resource.getCurrent() );
        }
    };

    Glide
        .with( context.getApplicationContext() ) // safer!
        .load( eatFoodyImages[2] )
        .into( viewTarget );
}

在 target 回调方法中,我们使用我们创建的方法 setImage(Drawable drawable) 在自定义 view 类中去设置图片。另外确保你注意到我们必须在 ViewTarget 的构造函数中传递我们自定义 view 作为参数: new ViewTarget<FutureStudioView, GlideDrawable>(customView)

NotificationTarget

下面我们用 RemoteViews 创建自定义界面的通知栏,布局和代码如下:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout  
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="2dp">
        <ImageView
            android:id="@+id/remoteview_notification_icon"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginRight="2dp"
            android:layout_weight="0"
            android:scaleType="centerCrop"/>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">
            <TextView
                android:id="@+id/remoteview_notification_headline"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:singleLine="true"
                android:textSize="12sp"/>
            <TextView
                android:id="@+id/remoteview_notification_short_message"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:paddingBottom="2dp"
                android:singleLine="true"
                android:textSize="14sp"
                android:textStyle="bold"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>  

final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.remoteview_notification);

rv.setImageViewResource(R.id.remoteview_notification_icon, R.mipmap.future_studio_launcher);

rv.setTextViewText(R.id.remoteview_notification_headline, "Headline");  
rv.setTextViewText(R.id.remoteview_notification_short_message, "Short Message");

// build notification
NotificationCompat.Builder mBuilder =  
new NotificationCompat.Builder(context)
    .setSmallIcon(R.mipmap.future_studio_launcher)
    .setContentTitle("Content Title")
    .setContentText("Content Text")
    .setContent(rv)
    .setPriority( NotificationCompat.PRIORITY_MIN);

final Notification notification = mBuilder.build();

// set big content view for newer androids
if (android.os.Build.VERSION.SDK_INT >= 16) {  
    notification.bigContentView = rv;
}

NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);  
mNotificationManager.notify(NOTIFICATION_ID, notification); 

这个代码片段为我们创建了三个重要的对象, notification 和 RemoteViews 以及常量 NOTIFICATION_ID。我们会需要这些去创建一个 NotificationTarget。

private NotificationTarget notificationTarget;

...

notificationTarget = new NotificationTarget(  
    context,
    rv,
    R.id.remoteview_notification_icon,
    notification,
    NOTIFICATION_ID); 

最后,我们像之前那样加载图片即可,只不过 into 传入的是 NotificationTarget 。

App Widgets

应用小部件加载网络图片,可以使用 Glide 提供的 AppWidgetTarget 来实现。

public class FSAppWidgetProvider extends AppWidgetProvider {

private AppWidgetTarget appWidgetTarget;

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                     int[] appWidgetIds) {

    RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.custom_view_futurestudio);

    appWidgetTarget = new AppWidgetTarget( context, rv, R.id.custom_view_image, appWidgetIds );

    Glide
            .with( context.getApplicationContext() ) // safer!
            .load( GlideExampleActivity.eatFoodyImages[3] )
            .asBitmap()
            .into( appWidgetTarget );

    pushWidgetUpdate(context, rv);
}

public static void pushWidgetUpdate(Context context, RemoteViews rv) {
    ComponentName myWidget = new ComponentName(context, FSAppWidgetProvider.class);
    AppWidgetManager manager = AppWidgetManager.getInstance(context);
    manager.updateAppWidget(myWidget, rv);
}
}

只需要把对应要传的参数都传入 Targets 即可,所有的操作都由 Glide 来完成 。

回调监听

Glide 的回调监听跟大多数的回调一样,创建一个监听并传入 .listener() 方法即可。监听器用字段声明的形式,跟之前一样。

RequestListener<String, Bitmap> requestListener = new RequestListener<String, Bitmap>() {
        @Override
        public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {
            ToastUtils.showShortToast(MainActivity.this,e.getMessage());
            return false;
        }

        @Override
        public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
            return false;
        }
    };

    Glide.with(this)
            .load(url2)
            .asBitmap()
            .listener(requestListener)
            .into(drawableTarget);

上述回调中,onException 是加载错误时会回调,默认返回 false ,这样 Glide 会显示错误占位图等,返回 true 则没有后续处理。onResourceReady 是加载成功时会回调,我们可以在这里拿到 Bitmap 做相应处理。