1. 程式人生 > >Glide 入門到精通之九——SimpleTarget 和 ViewTarget 用於自定義檢視類

Glide 入門到精通之九——SimpleTarget 和 ViewTarget 用於自定義檢視類


目錄(?)[+]

  1. Glide 中的回撥Targets
  2. SimpleTarget
  3. 關注 Targets
  4. Target 指定尺寸
  5. ViewTarget
  6. Outlook

http://mrfu.me/2016/02/27/Glide_Thumbnails/

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 ); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

這段程式碼的第一部分建立了一個欄位物件,聲明瞭一個方法,即一旦 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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

ViewTarget

我們不能直接使用 ImageView 的原因可能是多種多樣的。我們已經向你展示如何去接收一個 Bitmap。現在我們要更進一步。假設你有一個 Custom View。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 );
    }
}
  • 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
  • 27
  • 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
  • 27

你不能使用常規的 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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在 target 回撥方法中,我們使用我們建立的方法 setImage(Drawable drawable) 在自定義 view 類中去設定圖片。另外確保你注意到我們必須在ViewTarget 的建構函式中傳遞我們自定義 view 作為引數:new ViewTarget<FutureStudioView, GlideDrawable>(customView)

這應該涵蓋了所有你需要的自定義 view。你也可以在回撥中新增額外的工作。如,我們可以分析傳入的 Bitmap 的主要的顏色並設定十六進位制值給 TextView。但我們相信你應該已經有一些想法了。

http://mrfu.me/2016/02/27/Glide_Thumbnails/

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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

這段程式碼的第一部分建立了一個欄位物件,聲明瞭一個方法,即一旦 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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

ViewTarget

我們不能直接使用 ImageView 的原因可能是多種多樣的。我們已經向你展示如何去接收一個 Bitmap。現在我們要更進一步。假設你有一個 Custom View。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 );
    }
}
  • 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
  • 27
  • 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
  • 27

你不能使用常規的 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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在 target 回撥方法中,我們使用我們建立的方法 setImage(Drawable drawable) 在自定義 view 類中去設定圖片。另外確保你注意到我們必須在ViewTarget 的建構函式中傳遞我們自定義 view 作為引數:new ViewTarget<FutureStudioView, GlideDrawable>(customView)

這應該涵蓋了所有你需要的自定義 view。你也可以在回撥中新增額外的工作。如,我們可以分析傳入的 Bitmap 的主要的顏色並設定十六進位制值給 TextView。但我們相信你應該已經有一些想法了。