Glide入门教程(8)定制view中使用SimpleTarget和ViewTarget

       目前为止,我们总是假设加载图片或Gif到ImageView上,但那并不适用所有的情况。本文将要介绍在没有指定的ImageView时,获取一个图片的资源。

       我们已经能够很方便地使用Glide去加载图片到ImageView中。在应用场景中,Glide隐藏了大量复杂的工作。Glide在后台线程中处理了所有的网络请求,一旦结果准备完毕,就会调用UI线程更新ImageVIew。

       假设我们并没有ImageView作为图片加载的目标。我们只需要Bitmap本身。Glide提供了一个用Target获取Bitmap资源的方法。Target只是用来回调,它会在所有的加载和处理完毕时返回想要的结果。

       Glide提供了多种多样有各自明确目的Target。我们先从SimpleTarget介绍。

SimpleTarget

       代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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注意事项

       第一个是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的生命周期以外,使用下面的代码:

1
2
3
4
5
6
7
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并不起效。但是,如果你有个期望的具体大小,你可以增强回调。如果你知道图片应当为多大,那么在你的回调定义里应当指明,以节省内存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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( 250, 250 )。目前已经介绍了所有关于SimpleTarget的知识点。

ViewTarget

       有很多原因导致我们不能直接使用ImageView。前面已经介绍了如何获取Bitmap。假设你有个自定义的View,由于没有已知的方法在哪里设置图片,Glide并不支持加载图片到定制的View内。然而用ViewTarget会让这个问题更简单。

       先看一个简单的定制View,它继承于FrameLayout,内部使用了一个ImageView,上面覆盖了一个TextView:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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入门教程——10. 回调:定制view中使用SimpleTarget和ViewTarget

Fork me on GitHub