Glide 知識梳理(2) 自定義Target

澤毛發表於2017-12-21

一、概述

首先,我們回顧一下Glide的基本用法,我們的最後一步都是呼叫into(ImageView imageView),通過斷點,我們可以看到正是這一步觸發了圖片的請求:

Glide 知識梳理(2)   自定義Target
這個ImageView最終會通過buildTarget方法,封裝在GlideDrawableImageViewTarget當中,然後呼叫GenericRequestBuilder#into方法發起請求,Glide一直跟蹤這個Target,並在獲得圖片資源之後,通知Target來更新它內部持有的ImageView的引用。 這個過程就好像是我們平時請求網路時,會傳入一個Callback,等到非同步的操作執行完畢後,通過Callback傳回請求的資源來更新UI。 通過原始碼,可以看到和Target相關的類有:

  • Target
  • BaseTarget
    • SimpleTargetAppWidgetTargetPreloadTatgetNotificationTarget
    • ViewTarget
      • ImageViewTargetGlideDrawableImageViewTargetBitmapImageViewTargetDrawableImageViewTarget

今天這篇文章,我們就來學習一下SimpleTargetViewTarget的用法,主要參考了下面連結中的文章:

https://futurestud.io/tutorials/glide-callbacks-simpletarget-and-viewtarget-for-custom-view-classes

二、SimpleTarget

SimpleTarget可以看作是請求的回撥,我們在回撥當中進行處理,而不是傳入ImageViewGlide去負責更新。

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中傳入了ActivityContext,那麼Glide就會監聽Activity生命週期的變化,當Activity退到後臺之後,停止該請求。如果你希望它獨立於Activity的生命週期,那麼需要傳入一個ApplicationContext

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內部的方法。

四、小結

  • 從繼承樹上來看,SimpleTargetViewTarget是層次最低的可實現類,也是我們平時開發中比較常用的類。
  • 這兩者的區別就是ViewTarget內部的實現更加複雜,它會持有View的引用,並通過內部的SizeDeterminer計算View的寬高來提供給Glide作為參考,SimpleTarget則不會去處理這些邏輯,我們需要手動的指定一個寬高,所以,我們需要根據不同的使用場景來決定繼承於哪個Target來實現自己的業務邏輯。
  • 除了SimpleTargetViewTargetGlide還提供了繼承於它們的Target來簡化我們的操作。例如,更新通知欄和桌面外掛,從原始碼來看,它們是繼承於SimpleTarget,其最基本的原理和我們自定義SimpleTarget都是相同的,只是在回撥裡面呼叫AppWidgetManager/NotificationManager增加了更新相應元件的操作。在這裡就不多介紹了,有需要了解的可以看下面這篇文章:

https://futurestud.io/tutorials/glide-loading-images-into-notifications-and-appwidgets

相關文章