一、概述
首先,我們回顧一下Glide的基本用法,我們的最后一步都是調(diào)用into(ImageView imageView),通過斷點,我們可以看到正是這一步觸發(fā)了圖片的請求:

這個
ImageView最終會通過buildTarget方法,封裝在GlideDrawableImageViewTarget當(dāng)中,然后調(diào)用GenericRequestBuilder#into方法發(fā)起請求,Glide一直跟蹤這個Target,并在獲得圖片資源之后,通知Target來更新它內(nèi)部持有的ImageView的引用。這個過程就好像是我們平時請求網(wǎng)絡(luò)時,會傳入一個
Callback,等到異步的操作執(zhí)行完畢后,通過Callback傳回請求的資源來更新UI。通過源碼,可以看到和
Target相關(guān)的類有:
Target-
BaseTarget-
SimpleTarget:AppWidgetTarget、PreloadTatget、NotificationTarget -
ViewTarget-
ImageViewTarget:GlideDrawableImageViewTarget、BitmapImageViewTarget、DrawableImageViewTarget
-
-
今天這篇文章,我們就來學(xué)習(xí)一下SimpleTarget和ViewTarget的用法,主要參考了下面鏈接中的文章:
https://futurestud.io/tutorials/glide-callbacks-simpletarget-and-viewtarget-for-custom-view-classes
二、SimpleTarget
SimpleTarget可以看作是請求的回調(diào),我們在回調(diào)當(dāng)中進行處理,而不是傳入ImageView讓Glide去負責(zé)更新。
2.1 基本用法
先看一下SimpleTarget的用法:
public void loadSimpleTarget(View view) {
MySimpleTarget mySimpleTarget = new MySimpleTarget();
Glide.with(this)
.load(R.drawable.book_url)
.into(mySimpleTarget);
}
private class MySimpleTarget extends SimpleTarget<GlideDrawable> {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
mImageView.setImageDrawable(resource.getCurrent());
}
}
我們首先定義了一個SimpleTarget,然后把它通過into方法傳入。這樣當(dāng)Glide去服務(wù)器請求圖片成功之后,它會把請求到的圖片資源作為GlideDrawable傳遞回來,你可以使用這個GlideDrawable.getCurrent()進行自己想要的操作。
關(guān)于上面SimpleTarget的使用需要知道兩點:
- 由于我們把
SimpleTarget定義成了局部變量,那么它很有可能會被回收,一旦它被回收,那么我們收不到任何的回調(diào)了。 - 我們在
with中傳入了Activity的Context,那么Glide就會監(jiān)聽Activity生命周期的變化,當(dāng)Activity退到后臺之后,停止該請求。如果你希望它獨立于Activity的生命周期,那么需要傳入一個Application的Context。
2.2 設(shè)置資源的大小
當(dāng)我們傳入ImageView時,Glide會根據(jù)ImageView的大小來自動調(diào)整緩存的圖片資源大小,而當(dāng)我們使用Target的時候,并沒有提供這個條件來給Glide,因此,為了縮短處理時間和減少內(nèi)存,我們可以按下面的方法來指定緩存的大?。?/p>
public void loadSimpleTarget(View view) {
MySimpleTarget mySimpleTarget = new MySimpleTarget(50, 50);
Glide.with(this)
.load(R.drawable.book_url)
.into(mySimpleTarget);
}
private class MySimpleTarget extends SimpleTarget<GlideDrawable> {
public MySimpleTarget(int width, int height) {
super(width, height);
}
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
mImageView.setImageDrawable(resource.getCurrent());
}
}
三、ViewTarget
在上面一節(jié)中,我們展示了如何通過設(shè)置回調(diào)來獲得最終的Bitmap,但是上面的方法就是有一個缺陷:回調(diào)中只能拿到最終請求到的資源。我們需要持有View的全局對象,這樣才能在收到回調(diào)之后更新它,并且,Glide無法根據(jù)View的實際寬高來決定緩存圖片的大小。
ViewTarget就提供了這樣一種方案:我們在構(gòu)造Target時就傳入自定義的View,這樣在回調(diào)時就可以通過它來更新UI。
它的原理其實和我們開頭說到的傳入ImageView的原理類似,就是通過傳入Target的方式,但是ViewTarget會持有需要更新的View實例,這樣在回調(diào)時候,我們就能執(zhí)行自己需要的操作了,下面是使用ViewTarget的例子:
首先,定義一個自定義的View:
public class CustomView extends LinearLayout {
private ImageView mImageView;
private TextView mTextView;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
init();
}
private void init() {
mTextView = (TextView) findViewById(R.id.tv_custom_result);
mImageView = (ImageView) findViewById(R.id.iv_custom_result);
}
public void setResult(Drawable drawable) {
mTextView.setText("load success");
mImageView.setImageDrawable(drawable);
}
}
在布局當(dāng)中這么定義它:
<com.example.lizejun.repoglidelearn.CustomView
android:id="@+id/cv_result"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_custom_result"
android:layout_width="match_parent"
android:layout_height="30dp"
style="@style/style_tv_normal"/>
<ImageView
android:id="@+id/iv_custom_result"
android:layout_width="200dp"
android:layout_height="200dp" />
</com.example.lizejun.repoglidelearn.CustomView>
之后,我們定義一個ViewTarget,并加載它:
public void loadViewTarget(View view) {
CustomView customView = (CustomView) findViewById(R.id.cv_result);
MyViewTarget myViewTarget = new MyViewTarget(customView);
Glide.with(this)
.load(R.drawable.book_url)
.into(myViewTarget);
}
private class MyViewTarget extends ViewTarget<CustomView, GlideDrawable> {
public MyViewTarget(CustomView customView) {
super(customView);
}
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
view.setResult(resource.getCurrent());
}
}
它的整個步驟為:
- 首先獲得這個自定義
View實例。 - 之后把這個自定義
View作為ViewTarget的構(gòu)造函數(shù)的參數(shù),新建一個ViewTarget實例。 - 把這個
ViewTarget通過into()方法傳給Glide。 - 等待
Glide請求完畢,那么會回調(diào)ViewTarget中的onResourceReady方法,該Target中有我們傳入的自定義View,這樣,我們就可以調(diào)用這個自定義View內(nèi)部的方法。
四、小結(jié)
- 從繼承樹上來看,
SimpleTarget和ViewTarget是層次最低的可實現(xiàn)類,也是我們平時開發(fā)中比較常用的類。 - 這兩者的區(qū)別就是
ViewTarget內(nèi)部的實現(xiàn)更加復(fù)雜,它會持有View的引用,并通過內(nèi)部的SizeDeterminer計算View的寬高來提供給Glide作為參考,SimpleTarget則不會去處理這些邏輯,我們需要手動的指定一個寬高,所以,我們需要根據(jù)不同的使用場景來決定繼承于哪個Target來實現(xiàn)自己的業(yè)務(wù)邏輯。 - 除了
SimpleTarget和ViewTarget,Glide還提供了繼承于它們的Target來簡化我們的操作。例如,更新通知欄和桌面插件,從源碼來看,它們是繼承于SimpleTarget,其最基本的原理和我們自定義SimpleTarget都是相同的,只是在回調(diào)里面調(diào)用AppWidgetManager/NotificationManager增加了更新相應(yīng)組件的操作。在這里就不多介紹了,有需要了解的可以看下面這篇文章:
https://futurestud.io/tutorials/glide-loading-images-into-notifications-and-appwidgets