一、概述
首先,我們回顧一下Glide
的基本用法,我們的最後一步都是呼叫into(ImageView imageView)
,通過斷點,我們可以看到正是這一步觸發了圖片的請求:
ImageView
最終會通過buildTarget
方法,封裝在GlideDrawableImageViewTarget
當中,然後呼叫GenericRequestBuilder#into
方法發起請求,Glide
一直跟蹤這個Target
,並在獲得圖片資源之後,通知Target
來更新它內部持有的ImageView
的引用。
這個過程就好像是我們平時請求網路時,會傳入一個Callback
,等到非同步的操作執行完畢後,通過Callback
傳回請求的資源來更新UI
。
通過原始碼,可以看到和Target
相關的類有:
Target
BaseTarget
SimpleTarget
:AppWidgetTarget
、PreloadTatget
、NotificationTarget
ViewTarget
ImageViewTarget
:GlideDrawableImageViewTarget
、BitmapImageViewTarget
、DrawableImageViewTarget
今天這篇文章,我們就來學習一下SimpleTarget
和ViewTarget
的用法,主要參考了下面連結中的文章:
https://futurestud.io/tutorials/glide-callbacks-simpletarget-and-viewtarget-for-custom-view-classes
二、SimpleTarget
SimpleTarget
可以看作是請求的回撥,我們在回撥當中進行處理,而不是傳入ImageView
讓Glide
去負責更新。
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
方法傳入。這樣當Glide
去伺服器請求圖片成功之後,它會把請求到的圖片資源作為GlideDrawable
傳遞回來,你可以使用這個GlideDrawable.getCurrent()
進行自己想要的操作。
關於上面SimpleTarget
的使用需要知道兩點:
- 由於我們把
SimpleTarget
定義成了區域性變數,那麼它很有可能會被回收,一旦它被回收,那麼我們收不到任何的回撥了。 - 我們在
with
中傳入了Activity
的Context
,那麼Glide
就會監聽Activity
生命週期的變化,當Activity
退到後臺之後,停止該請求。如果你希望它獨立於Activity
的生命週期,那麼需要傳入一個Application
的Context
。
2.2 設定資源的大小
當我們傳入ImageView
時,Glide
會根據ImageView
的大小來自動調整快取的圖片資源大小,而當我們使用Target
的時候,並沒有提供這個條件來給Glide
,因此,為了縮短處理時間和減少記憶體,我們可以按下面的方法來指定快取的大小:
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
在上面一節中,我們展示瞭如何通過設定回撥來獲得最終的Bitmap
,但是上面的方法就是有一個缺陷:回撥中只能拿到最終請求到的資源。我們需要持有View
的全域性物件,這樣才能在收到回撥之後更新它,並且,Glide
無法根據View
的實際寬高來決定快取圖片的大小。
ViewTarget
就提供了這樣一種方案:我們在構造Target
時就傳入自定義的View
,這樣在回撥時就可以通過它來更新UI
。
它的原理其實和我們開頭說到的傳入ImageView
的原理類似,就是通過傳入Target
的方式,但是ViewTarget
會持有需要更新的View
例項,這樣在回撥時候,我們就能執行自己需要的操作了,下面是使用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);
}
}
複製程式碼
在佈局當中這麼定義它:
<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
的建構函式的引數,新建一個ViewTarget
例項。 - 把這個
ViewTarget
通過into()
方法傳給Glide
。 - 等待
Glide
請求完畢,那麼會回撥ViewTarget
中的onResourceReady
方法,該Target
中有我們傳入的自定義View
,這樣,我們就可以呼叫這個自定義View
內部的方法。
四、小結
- 從繼承樹上來看,
SimpleTarget
和ViewTarget
是層次最低的可實現類,也是我們平時開發中比較常用的類。 - 這兩者的區別就是
ViewTarget
內部的實現更加複雜,它會持有View
的引用,並通過內部的SizeDeterminer
計算View
的寬高來提供給Glide
作為參考,SimpleTarget
則不會去處理這些邏輯,我們需要手動的指定一個寬高,所以,我們需要根據不同的使用場景來決定繼承於哪個Target
來實現自己的業務邏輯。 - 除了
SimpleTarget
和ViewTarget
,Glide
還提供了繼承於它們的Target
來簡化我們的操作。例如,更新通知欄和桌面外掛,從原始碼來看,它們是繼承於SimpleTarget
,其最基本的原理和我們自定義SimpleTarget
都是相同的,只是在回撥裡面呼叫AppWidgetManager/NotificationManager
增加了更新相應元件的操作。在這裡就不多介紹了,有需要了解的可以看下面這篇文章:
https://futurestud.io/tutorials/glide-loading-images-into-notifications-and-appwidgets