在前三篇博客圍繞著 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 的實例,當我們看如何加載圖像到通知中以及應用程序窗口小部件里。
- 前言
- 一開始
- 二加載進階
- 三ListAdapter(ListView, GridView)
- 四占位符 和 漸現動畫
- 五圖片重設大小 和 縮放
- 六顯示 Gif 和 Video
- 七緩存基礎
- 八請求優先級
- 九縮略圖
- 十回調:SimpleTarget 和 ViewTarget 用于自定義視圖類
- 十一加載圖片到通知欄和應用小部件中
- 十二異常:調試和錯誤處理
- 十三自定義轉換
- 十四用 animate() 自定義動畫
- 十五集成網絡棧
- 十六用 Module 自定義
- 十七Module 實例:接受自簽名證書的 HTTPS
- 十八Module 實例:自定義緩存
- 十九Module 實例:用自定義尺寸優化加載的圖片
- 二十動態使用 Model Loader
- 二十一如何旋轉圖像
- 二十二系列綜述