ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
在前三篇博客围绕着 Glide 做了优化并提高了用户体验,在接下来的几篇博客中将会用到 Glide 的回调技术。目前为止,我们总是假设我们要加载图片或者 Gif 到 ImageView 中。但这并非总是如此。这篇博客中,我们将看看没有指定一个 ImageView 而来获取图片资源的 Bitmap 的方法。 **Glide 中的回调:Targets** 目前为止,我们很方便的使用 Glide 建造者去加载图片到 `ImageView` 中了。Glide 隐藏了一大堆复杂的在后台的场景。Glide 做了所有的网络请求和处理在后台线程中,一旦结果准备好了之后,切回到 UI 线程然后更新 ImageView。 在这篇博客中,我们假定 `ImageView` 不再是图像的最后一步。我们只要 Bitmap 本身。Glide 提供了一个用 `Targets` 的简单的方式去接受图片资源的 Bitmap。Targets 是没有任何别的回调,它在 Glide 做完所有的加载和处理之后返回结果。 Glide 提供了各种的 targets 并且每个都有其明确的目的。我们将在接下来的几节中通过使用它们。让我们从 `SimpleTarget` 开始。 **SimpleTarget** 看如下代码实例: ~~~ private SimpleTarget target = new SimpleTarget<Bitmap>() { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) { // do something with the bitmap // for demonstration purposes, let's just set it to an ImageView imageView1.setImageBitmap( bitmap ); } }; private void loadImageSimpleTarget() { Glide .with( context ) // could be an issue! .load( eatFoodyImages[0] ) .asBitmap() .into( target ); } ~~~ 这段代码的第一部分创建了一个字段对象,声明了一个方法,即一旦 Glide 已加载并处理完图像,它将被调用。这个回调方法传了 Bitmap 作为一个参数。你之后便可以使用这个 Bitmap 对象,无论你要怎样用它。 这段代码的第二部分是我们如何通过 Glide 用 targets:和 `ImageView` 用法完全相同的!你既可以传一个 Target 也可以传一个 `ImageView` 参数给 `.into()` 方法。Glide 自己将会处理并返回结果给任何一个。这里有一些不同的是,我们添加了 `.asBitmap()`,它强制 Glide 去返回一个 `Bitmap` 对象。记住,Glide 也可以加载 Gif 或 video 的。为了防止 target 的冲突(我们需要 Bitmap) 和未知资源在网络背后的 URL(可能是一个 Gif),我们可以调用 `.asBitmap()` 告诉 Glide 我们需要一个图像。 **关注 Targets** 除了知道如何实现一个简单版本的 Glide 的 Target 回调系统,你要学会额外两件事。 首先是 `SimpleTarget` 对象的字段声明。从技术上来说,Java/Android 会允许你在 `.into()` 方法中去声明 target 的匿名内部类。然而,这大大增加了这样一个可能性:即在 Glide 做完图片请求之前, Android 垃圾回收移除了这个匿名内部类对象。最终这可能会导致一个情况,当图像加载完成了,但是回调再也不会被调用。所请确保你所声明的回调对象是作为一个字段对象的,这样你就可以保护它避免被邪恶的 Android 垃圾回收机制回收 ;-) 第二个关键部分是 Glide 建造者中这行:`.with(context)`。 这里的问题实际是 Glide 的功能:当你传了一个 context,例如是当前应用的 activity,Glide 将会自动停止请求当请求的 activity 已经停止的时候。这整合到了应用的生命周期中通常是非常有帮助的,但是有时工作起来是困难的,如果你的 target 是独立于应用的 activity 生命周期。这里的解决方案是用 application 的 context: `.with(context.getApplicationContext))`。当应用资深完全停止时,Glide 才会杀死这个图片请求。请求记住,再说一次,如果你的请求需要在 activity 生命周期之外去做时,才用下面这样的代码: ~~~ private void loadImageSimpleTargetApplicationContext() { Glide .with( context.getApplicationContext() ) // safer! .load( eatFoodyImages[1] .asBitmap() .into( target2 ); } ~~~ **Target 指定尺寸** 另一个潜在的问题是,target 没有指明大小。如果你你传一个 `ImageView` 作为参数给 `.into()`,Glide 将会用 `ImageView` 的大小去限制图像的大小。比如说,如果加载的图片是 1000x1000 像素的,但是 `ImageView` 只有 250x250 像素,Glide 将会减少图片的尺寸去节省时间和内存。很显然,在和 target 协作的时候并没有这么做,因为我们并没有已知的大小。然而,如果你有一个指定的大小,你可以提高回调。如果你知道这种图片应该要多大,你应该在你的回调声明中指定它以节省一些内存。 ~~~ private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) { @Override public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) { imageView2.setImageBitmap( bitmap ); } }; private void loadImageSimpleTargetApplicationContext() { Glide .with( context.getApplicationContext() ) // safer! .load( eatFoodyImages[1] ) .asBitmap() .into( target2 ); } ~~~ **ViewTarget** 我们不能直接使用 ImageView 的原因可能是多种多样的。我们已经向你展示如果去接收一个 Bitmap。现在我们要更进一步。假设你有一个 [Custom View](https://developer.android.com/training/custom-views/index.html?hl=zh-cn)。Glide 并不支持加载图片到自定义 view 中,因为并没有方法知道图片应该在哪里被设置。然而,Glide 可以用 `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)`。 这应该涵盖了所有你需要的自定义 view。你也可以在回调中添加额外的工作。如,我们可以分析传入的 Bitmap 的主要的颜色并设置十六进制值给 `TextView`。但我们相信你应该已经有一些想法了。 **Outlook** 在这篇相当长的博客中,你已经了解了在 Glide 中 target 的基本原理。你已经学会了如何访问图片的 Bitmap 以及如何将图片加载到你自定义的 view 中。我们错过什么了吗?让在评论中让我们知晓! 下周,我们将继续 target 的实例,当我们看如何加载图像到通知中以及应用程序窗口小部件里。