Glide — 回调:定制view中使用SimpleTarget和ViewTarget
原文:Callbacks: SimpleTarget and ViewTarget for Custom View Classes
作者:Norman Peitek
翻译:Dexter0218
前面3篇都是围绕着优化Glide的流程和提升用户体验。下面的几篇会如何使用Glide中的的回调技术。目前为止,我们总是假设加载图片或Gif到ImageView上,但那并不适用所有的情况。本文将要介绍在没有指定的ImageView时,着手获取一个图片的资源。
Glide 系列概览
- 入门简介
- 高级加载
- 适配器(ListView, GridView)
- 占位图& 淡入淡出动画
- 图片大小 & 缩放
- 播放GIF & 视频
- 缓存基础
- 请求优先级
- 缩略图
- 回调:定制view中使用SimpleTarget和ViewTarget
- 通知栏和桌面小控件的图片加载
- 异常: 调试和报错处理
- 自定义变换
- 用animate()定制动画
- 整合网络协议栈
- 用Modules定制Glide
- Glide Module 案例: 接受自签名HTTPS证书
- Glide Module 案例: 自定义缓存
- Glide Module 案例: 通过加载自定义大小图片优化
- 动态使用 Model Loaders
- 如何旋转图片
- 系列综述
Glide中的回调:Target
目前为止,我们已经能够很方便地使用Glide去加载图片到ImageView中。在应用场景中,Glide隐藏了大量复杂的工作。Glide在后台线程中处理了所有的网络请求,一旦结果准备完毕,就会调用UI线程更新ImageVIew。
在这篇文章中,我们假设我们并没有ImageView作为图片加载的目标。我们只需要Bitmap本身。Glide提供了一个用Target获取Bitmap资源的方法。Target只是用来回调,它会在所有的加载和处理完毕时返回想要的结果。
Glide提供了多种多样有各自明确目的Target。在随后的几个小节了逐一介绍。我们先从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 );
}
代码的第一部分,创建一个target字段对象,里面定义了个方法,这个方法一旦Glide加载和处理完图片将会被调用。回调方法传回Bitmap作为参数,你可以在你所需要用的地方随意使用这个Bitmap对象。
代码的第二部分,表明了Glide里如何使用Target,明显跟ImageView一样!你可以传递一个Target或者ImageView作为参数到.into()方法里。Glide会神奇地将结果返回。这里有个不同点,我们添加了.asBitmap(),这会强制返回一个Bitmap对象。记住,Glide也可以加载Gif或视频。为了防止在从网络URL(可能是GIF)获取Bitmap时,出现未知格式图片冲突(期望是Bitmap),我们设置.asBitmap()去告诉Glide只有在资源是一个图片是才算成功,其他的都算解析失败。
使用Target注意事项
除了刚介绍的Target的回调系统的一个简单版本,你要学会额外两件事。
第一个是SimpleTarget对象的定义。java/Android可以允许你在.into()内匿名定义,但这会显著增加在Glide处理完图片请求前Android垃圾回收清理匿名target对象的可能性。最终,会导致图片被加载了,但是回调永远不会被调用。所以,请确保将你的回调定义为一个字段对象,防止被万恶的Android垃圾回收给清理掉。
第二个关键部分是Glide的.with( context )。这个问题实际上是Glide一个特性问题:当你传递了一个context,例如当前app的activity,当activity停止后,Glide会自动停止当前的请求。这种整合到app生命周期内是非常有用的,但也是很难处理的。如果你的target是独立于app的生命周期。这里的解决方案是使用application的context:.with( context.getApplicationContext() )
。当app自己停止运行的时候,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没有具体大小,这对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 );
}
和“普通”target唯一不同的是这个以像素为单位的图片大小声明:new SimpleTarget<Bitmap>( 250, 250 )
。目前已经介绍了所有关于SimpleTarget的知识点。
ViewTarget
有很多原因导致我们不能直接使用ImageView。前面已经介绍了如何获取Bitmap。现在,我们将更深入学习。假设你有个自定义的View。由于没有已知的方法在哪里设置图片,Glide并不支持加载图片到定制的View内。然而用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 );
}
}
由于我们定制的view并不是继承自ImageView,这里不能使用常规的.into()方法。因此,我们只能创建一个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的回调方法中,我们在定制view上使用我们创建的setImage(Drawable drawable)
方法设置图片。同时,确保你注意到我们已经在ViewTarget的构造方法里传递了我们的定制view:new ViewTarget<FutureStudioView, GlideDrawable>( customView )
。
这应该覆盖了所有你可能需要定制的view。你也可以在回调中做额外的工作。例如,我们可以解析即将到来的图片的主色调,然后设置TextView的颜色。但我们相信你肯定已经有你自己的想法。
展望
在这篇较长的文章中,你已经学会了Glide中target的基础。你已经学会了从图片获取Bitmap,并加载到你定制的view中。可以在评论中告诉我们我们忽略了什么。
后面的文章,我们将要继续通过例子介绍target如何向通知栏和桌面小控件加载图片。