帶你一起走入Glide4

本篇文章是轉載郭霖的文章,原文中地址爲:http://blog.csdn.net/guolin_blog/article/details/78582548

Glide 4概述

剛纔有說到,有些朋友覺得Glide 4相對於Glide 3改動非常大,其實不然。之所以大家會有這種錯覺,是因爲你將Glide 3的用法直接搬到Glide 4中去使用,結果IDE全面報錯,然後大家可能就覺得Glide 4的用法完全變掉了。

其實Glide 4相對於Glide 3的變動並不大,只是你還沒有了解它的變動規則而已。一旦你掌握了Glide 4的變動規則之後,你會發現大多數Glide 3的用法放到Glide 4上都還是通用的。

我對Glide 4進行了一個大概的研究之後,發現Glide 4並不能算是有什麼突破性的升級,而更多是一些API工整方面的優化。相比於Glide 3的API,Glide 4進行了更加科學合理地調整,使得易讀性、易寫性、可擴展性等方面都有了不錯的提升。但如果你已經對Glide 3非常熟悉的話,並不是就必須要切換到Glide 4上來,因爲Glide 4上能實現的功能Glide 3也都能實現,而且Glide 4在性能方面也並沒有什麼提升。

但是對於新接觸Glide的朋友而言,那就沒必要再去學習Glide 3了,直接上手Glide 4就是最佳的選擇了。

好了,對Glide 4進行一個基本的概述之後,接下來我們就要正式開始學習它的用法了。剛纔我已經說了,Glide 4的用法相對於Glide 3其實改動並不大。在前面的七篇文章中,我們已經學習了Glide 3的基本用法、緩存機制、回調與監聽、圖片變換、自定義模塊等用法,那麼今天這篇文章的目標就很簡單了,就是要掌握如何在Glide 4上實現之前所學習過的所有功能,那麼我們現在就開始吧。

開始

要想使用Glide,首先需要將這個庫引入到我們的項目當中。新建一個Glide4Test項目,然後在app/build.gradle文件當中添加如下依賴:

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.4.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.4.0'
}
  • 1
  • 2
  • 3
  • 4

注意,相比於Glide 3,這裏要多添加一個compiler的庫,這個庫是用於生成Generated API的,待會我們會講到它。

另外,Glide中需要用到網絡功能,因此你還得在AndroidManifest.xml中聲明一下網絡權限才行:

<uses-permission android:name="android.permission.INTERNET" />
  • 1

就是這麼簡單,然後我們就可以自由地使用Glide中的任意功能了。

加載圖片

現在我們就來嘗試一下如何使用Glide來加載圖片吧。比如這是一張圖片的地址:

http://guolin.tech/book.png
  • 1

然後我們想要在程序當中去加載這張圖片。

那麼首先打開項目的佈局文件,在佈局當中加入一個Button和一個ImageView,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load Image"
        android:onClick="loadImage"
        />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

爲了讓用戶點擊Button的時候能夠將剛纔的圖片顯示在ImageView上,我們需要修改MainActivity中的代碼,如下所示:

public class MainActivity extends AppCompatActivity {

    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.image_view);
    }

    public void loadImage(View view) {
        String url = "http://guolin.tech/book.png";
        Glide.with(this).load(url).into(imageView);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

沒錯,就是這麼簡單。現在我們來運行一下程序,效果如下圖所示:

可以看到,一張網絡上的圖片已經被成功下載,並且展示到ImageView上了。

你會發現,到目前爲止,Glide 4的用法和Glide 3是完全一樣的,實際上核心的代碼就只有這一行而已:

Glide.with(this).load(url).into(imageView);
  • 1

仍然還是傳統的三步走:先with(),再load(),最後into()。對這行代碼的解讀,我在 Android圖片加載框架最全解析(一),Glide的基本用法 這篇文章中講解的很清楚了,這裏就不再贅述。

好了,現在你已經成功入門Glide 4了,那麼接下來就讓我們學習一下Glide 4的更多用法吧。

佔位圖

觀察剛纔加載網絡圖片的效果,你會發現,點擊了Load Image按鈕之後,要稍微等一會圖片纔會顯示出來。這其實很容易理解,因爲從網絡上下載圖片本來就是需要時間的。那麼我們有沒有辦法再優化一下用戶體驗呢?當然可以,Glide提供了各種各樣非常豐富的API支持,其中就包括了佔位圖功能。

顧名思義,佔位圖就是指在圖片的加載過程中,我們先顯示一張臨時的圖片,等圖片加載出來了再替換成要加載的圖片。

下面我們就來學習一下Glide佔位圖功能的使用方法,首先我事先準備好了一張loading.jpg圖片,用來作爲佔位圖顯示。然後修改Glide加載部分的代碼,如下所示:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

沒錯,就是這麼簡單。這裏我們先創建了一個RequestOptions對象,然後調用它的placeholder()方法來指定佔位圖,再將佔位圖片的資源id傳入到這個方法中。最後,在Glide的三步走之間加入一個apply()方法,來應用我們剛纔創建的RequestOptions對象。

不過如果你現在重新運行一下代碼並點擊Load Image,很可能是根本看不到佔位圖效果的。因爲Glide有非常強大的緩存機制,我們剛纔加載圖片的時候Glide自動就已經將它緩存下來了,下次加載的時候將會直接從緩存中讀取,不會再去網絡下載了,因而加載的速度非常快,所以佔位圖可能根本來不及顯示。

因此這裏我們還需要稍微做一點修改,來讓佔位圖能有機會顯示出來,修改代碼如下所示:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading)
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到,這裏在RequestOptions對象中又串接了一個diskCacheStrategy()方法,並傳入DiskCacheStrategy.NONE參數,這樣就可以禁用掉Glide的緩存功能。

關於Glide緩存方面的內容我們待會兒會進行更詳細的講解,這裏只是爲了測試佔位圖功能而加的一個額外配置,暫時你只需要知道禁用緩存必須這麼寫就可以了。

現在重新運行一下代碼,效果如下圖所示:

可以看到,當點擊Load Image按鈕之後會立即顯示一張佔位圖,然後等真正的圖片加載完成之後會將佔位圖替換掉。

除了這種加載佔位圖之外,還有一種異常佔位圖。異常佔位圖就是指,如果因爲某些異常情況導致圖片加載失敗,比如說手機網絡信號不好,這個時候就顯示這張異常佔位圖。

異常佔位圖的用法相信你已經可以猜到了,首先準備一張error.jpg圖片,然後修改Glide加載部分的代碼,如下所示:

RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.ic_launcher_background)
        .error(R.drawable.error)
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

很簡單,這裏又串接了一個error()方法就可以指定異常佔位圖了。

其實看到這裏,如果你熟悉Glide 3的話,相信你已經掌握Glide 4的變化規律了。在Glide 3當中,像placeholder()、error()、diskCacheStrategy()等等一系列的API,都是直接串聯在Glide三步走方法中使用的。

而Glide 4中引入了一個RequestOptions對象,將這一系列的API都移動到了RequestOptions當中。這樣做的好處是可以使我們擺脫冗長的Glide加載語句,而且還能進行自己的API封裝,因爲RequestOptions是可以作爲參數傳入到方法中的。

比如你就可以寫出這樣的Glide加載工具類:

public class GlideUtil {

    public static void load(Context context,
                            String url,
                            ImageView imageView,
                            RequestOptions options) {
        Glide.with(context)
             .load(url)
             .apply(options)
             .into(imageView);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

指定圖片大小

實際上,使用Glide在大多數情況下我們都是不需要指定圖片大小的,因爲Glide會自動根據ImageView的大小來決定圖片的大小,以此保證圖片不會佔用過多的內存從而引發OOM。

不過,如果你真的有這樣的需求,必須給圖片指定一個固定的大小,Glide仍然是支持這個功能的。修改Glide加載部分的代碼,如下所示:

RequestOptions options = new RequestOptions()
        .override(200, 100);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

仍然非常簡單,這裏使用override()方法指定了一個圖片的尺寸。也就是說,Glide現在只會將圖片加載成200*100像素的尺寸,而不會管你的ImageView的大小是多少了。

如果你想加載一張圖片的原始尺寸的話,可以使用Target.SIZE_ORIGINAL關鍵字,如下所示:

RequestOptions options = new RequestOptions()
        .override(Target.SIZE_ORIGINAL);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

這樣的話,Glide就不會再去自動壓縮圖片,而是會去加載圖片的原始尺寸。當然,這種寫法也會面臨着更高的OOM風險。

緩存機制

Glide的緩存設計可以說是非常先進的,考慮的場景也很周全。在緩存這一功能上,Glide又將它分成了兩個模塊,一個是內存緩存,一個是硬盤緩存。

這兩個緩存模塊的作用各不相同,內存緩存的主要作用是防止應用重複將圖片數據讀取到內存當中,而硬盤緩存的主要作用是防止應用重複從網絡或其他地方重複下載和讀取數據。

內存緩存和硬盤緩存的相互結合才構成了Glide極佳的圖片緩存效果,那麼接下來我們就來分別學習一下這兩種緩存的使用方法。

首先來看內存緩存。

你要知道,默認情況下,Glide自動就是開啓內存緩存的。也就是說,當我們使用Glide加載了一張圖片之後,這張圖片就會被緩存到內存當中,只要在它還沒從內存中被清除之前,下次使用Glide再加載這張圖片都會直接從內存當中讀取,而不用重新從網絡或硬盤上讀取了,這樣無疑就可以大幅度提升圖片的加載效率。比方說你在一個RecyclerView當中反覆上下滑動,RecyclerView中只要是Glide加載過的圖片都可以直接從內存當中迅速讀取並展示出來,從而大大提升了用戶體驗。

而Glide最爲人性化的是,你甚至不需要編寫任何額外的代碼就能自動享受到這個極爲便利的內存緩存功能,因爲Glide默認就已經將它開啓了。

那麼既然已經默認開啓了這個功能,還有什麼可講的用法呢?只有一點,如果你有什麼特殊的原因需要禁用內存緩存功能,Glide對此提供了接口:

RequestOptions options = new RequestOptions()
        .skipMemoryCache(true);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以看到,只需要調用skipMemoryCache()方法並傳入true,就表示禁用掉Glide的內存緩存功能。

接下來我們開始學習硬盤緩存方面的內容。

其實在剛剛學習佔位圖功能的時候,我們就使用過硬盤緩存的功能了。當時爲了禁止Glide對圖片進行硬盤緩存而使用瞭如下代碼:

RequestOptions options = new RequestOptions()
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

調用diskCacheStrategy()方法並傳入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盤緩存功能了。

這個diskCacheStrategy()方法基本上就是Glide硬盤緩存功能的一切,它可以接收五種參數:

  • DiskCacheStrategy.NONE: 表示不緩存任何內容。
  • DiskCacheStrategy.DATA: 表示只緩存原始圖片。
  • DiskCacheStrategy.RESOURCE: 表示只緩存轉換過後的圖片。
  • DiskCacheStrategy.ALL : 表示既緩存原始圖片,也緩存轉換過後的圖片。
  • DiskCacheStrategy.AUTOMATIC: 表示讓Glide根據圖片資源智能地選擇使用哪一種緩存策略(默認選項)。

其中,DiskCacheStrategy.DATA對應Glide 3中的DiskCacheStrategy.SOURCE,DiskCacheStrategy.RESOURCE對應Glide 3中的DiskCacheStrategy.RESULT。而DiskCacheStrategy.AUTOMATIC是Glide 4中新增的一種緩存策略,並且在不指定diskCacheStrategy的情況下默認使用就是的這種緩存策略。

上面五種參數的解釋本身並沒有什麼難理解的地方,但是關於轉換過後的圖片這個概念大家可能需要了解一下。就是當我們使用Glide去加載一張圖片的時候,Glide默認並不會將原始圖片展示出來,而是會對圖片進行壓縮和轉換(我們會在稍後學習這方面的內容)。總之就是經過種種一系列操作之後得到的圖片,就叫轉換過後的圖片。

好的,關於Glide 4硬盤緩存的內容就講到這裏。想要了解更多Glide緩存方面的知識,可以參考 Android圖片加載框架最全解析(三),深入探究Glide的緩存機制 這篇文章。

指定加載格式

我們都知道,Glide其中一個非常亮眼的功能就是可以加載GIF圖片,而同樣作爲非常出色的圖片加載框架的Picasso是不支持這個功能的。

而且使用Glide加載GIF圖並不需要編寫什麼額外的代碼,Glide內部會自動判斷圖片格式。比如我們將加載圖片的URL地址改成一張GIF圖,如下所示:

Glide.with(this)
     .load("http://guolin.tech/test.gif")
     .into(imageView);
  • 1
  • 2
  • 3

現在重新運行一下代碼,效果如下圖所示:

也就是說,不管我們傳入的是一張普通圖片,還是一張GIF圖片,Glide都會自動進行判斷,並且可以正確地把它解析並展示出來。

但是如果我想指定加載格式該怎麼辦呢?就比如說,我希望加載的這張圖必須是一張靜態圖片,我不需要Glide自動幫我判斷它到底是靜圖還是GIF圖。

想實現這個功能仍然非常簡單,我們只需要再串接一個新的方法就可以了,如下所示:

Glide.with(this)
     .asBitmap()
     .load("http://guolin.tech/test.gif")
     .into(imageView);
  • 1
  • 2
  • 3
  • 4

可以看到,這裏在with()方法的後面加入了一個asBitmap()方法,這個方法的意思就是說這裏只允許加載靜態圖片,不需要Glide去幫我們自動進行圖片格式的判斷了。如果你傳入的還是一張GIF圖的話,Glide會展示這張GIF圖的第一幀,而不會去播放它。

熟悉Glide 3的朋友對asBitmap()方法肯定不會陌生對吧?但是千萬不要覺得這裏就沒有陷阱了,在Glide 3中的語法是先load()再asBitmap()的,而在Glide 4中是先asBitmap()再load()的。乍一看可能分辨不出來有什麼區別,但如果你寫錯了順序就肯定會報錯了。

那麼類似地,既然我們能強制指定加載靜態圖片,就也能強制指定加載動態圖片,對應的方法是asGif()。而Glide 4中又新增了asFile()方法和asDrawable()方法,分別用於強制指定文件格式的加載和Drawable格式的加載,用法都比較簡單,就不再進行演示了。

回調與監聽

回調與監聽這部分的內容稍微有點多,我們分成四部分來學習一下。

1. into()方法

我們都知道Glide的into()方法中是可以傳入ImageView的。那麼into()方法還可以傳入別的參數嗎?我們可以讓Glide加載出來的圖片不顯示到ImageView上嗎?答案是肯定的,這就需要用到自定義Target功能。

Glide中的Target功能多樣且複雜,下面我就先簡單演示一種SimpleTarget的用法吧,代碼如下所示:

SimpleTarget<Drawable> simpleTarget = new SimpleTarget<Drawable>() {
    @Override
    public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
        imageView.setImageDrawable(resource);
    }
};

public void loadImage(View view) {
    Glide.with(this)
         .load("http://guolin.tech/book.png")
         .into(simpleTarget);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這裏我們創建了一個SimpleTarget的實例,並且指定它的泛型是Drawable,然後重寫了onResourceReady()方法。在onResourceReady()方法中,我們就可以獲取到Glide加載出來的圖片對象了,也就是方法參數中傳過來的Drawable對象。有了這個對象之後你可以使用它進行任意的邏輯操作,這裏我只是簡單地把它顯示到了ImageView上。

SimpleTarget的實現創建好了,那麼只需要在加載圖片的時候將它傳入到into()方法中就可以了。

這裏限於篇幅原因我只演示了自定義Target的簡單用法,想學習更多相關的內容可以去閱讀 Android圖片加載框架最全解析(四),玩轉Glide的回調與監聽 。

2. preload()方法

Glide加載圖片雖說非常智能,它會自動判斷該圖片是否已經有緩存了,如果有的話就直接從緩存中讀取,沒有的話再從網絡去下載。但是如果我希望提前對圖片進行一個預加載,等真正需要加載圖片的時候就直接從緩存中讀取,不想再等待慢長的網絡加載時間了,這該怎麼辦呢?

不用擔心,Glide專門給我們提供了預加載的接口,也就是preload()方法,我們只需要直接使用就可以了。

preload()方法有兩個方法重載,一個不帶參數,表示將會加載圖片的原始尺寸,另一個可以通過參數指定加載圖片的寬和高。

preload()方法的用法也非常簡單,直接使用它來替換into()方法即可,如下所示:

Glide.with(this)
     .load("http://guolin.tech/book.png")
     .preload();
  • 1
  • 2
  • 3

調用了預加載之後,我們以後想再去加載這張圖片就會非常快了,因爲Glide會直接從緩存當中去讀取圖片並顯示出來,代碼如下所示:

Glide.with(this)
     .load("http://guolin.tech/book.png")
     .into(imageView);
  • 1
  • 2
  • 3

3. submit()方法

一直以來,我們使用Glide都是爲了將圖片顯示到界面上。雖然我們知道Glide會在圖片的加載過程中對圖片進行緩存,但是緩存文件到底是存在哪裏的,以及如何去直接訪問這些緩存文件?我們都還不知道。

其實Glide將圖片加載接口設計成這樣也是希望我們使用起來更加的方便,不用過多去考慮底層的實現細節。但如果我現在就是想要去訪問圖片的緩存文件該怎麼辦呢?這就需要用到submit()方法了。

submit()方法其實就是對應的Glide 3中的downloadOnly()方法,和preload()方法類似,submit()方法也是可以替換into()方法的,不過submit()方法的用法明顯要比preload()方法複雜不少。這個方法只會下載圖片,而不會對圖片進行加載。當圖片下載完成之後,我們可以得到圖片的存儲路徑,以便後續進行操作。

那麼首先我們還是先來看下基本用法。submit()方法有兩個方法重載:

  • submit()
  • submit(int width, int height)

其中submit()方法是用於下載原始尺寸的圖片,而submit(int width, int height)則可以指定下載圖片的尺寸。

這裏就以submit()方法來舉例。當調用了submit()方法後會立即返回一個FutureTarget對象,然後Glide會在後臺開始下載圖片文件。接下來我們調用FutureTarget的get()方法就可以去獲取下載好的圖片文件了,如果此時圖片還沒有下載完,那麼get()方法就會阻塞住,一直等到圖片下載完成纔會有值返回。

下面我們通過一個例子來演示一下吧,代碼如下所示:

public void downloadImage() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                String url = "http://www.guolin.tech/book.png";
                final Context context = getApplicationContext();
                FutureTarget<File> target = Glide.with(context)
                        .asFile()
                        .load(url)
                        .submit();
                final File imageFile = target.get();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, imageFile.getPath(), Toast.LENGTH_LONG).show();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

這段代碼稍微有一點點長,我帶着大家解讀一下。首先,submit()方法必須要用在子線程當中,因爲剛纔說了FutureTarget的get()方法是會阻塞線程的,因此這裏的第一步就是new了一個Thread。在子線程當中,我們先獲取了一個Application Context,這個時候不能再用Activity作爲Context了,因爲會有Activity銷燬了但子線程還沒執行完這種可能出現。

接下來就是Glide的基本用法,只不過將into()方法替換成了submit()方法,並且還使用了一個asFile()方法來指定加載格式。submit()方法會返回一個FutureTarget對象,這個時候其實Glide已經開始在後臺下載圖片了,我們隨時都可以調用FutureTarget的get()方法來獲取下載的圖片文件,只不過如果圖片還沒下載好線程會暫時阻塞住,等下載完成了纔會把圖片的File對象返回。

最後,我們使用runOnUiThread()切回到主線程,然後使用Toast將下載好的圖片文件路徑顯示出來。

現在重新運行一下代碼,效果如下圖所示。

這樣我們就能清晰地看出來圖片完整的緩存路徑是什麼了。

4. listener()方法

其實listener()方法的作用非常普遍,它可以用來監聽Glide加載圖片的狀態。舉個例子,比如說我們剛纔使用了preload()方法來對圖片進行預加載,但是我怎樣確定預加載有沒有完成呢?還有如果Glide加載圖片失敗了,我該怎樣調試錯誤的原因呢?答案都在listener()方法當中。

下面來看下listener()方法的基本用法吧,不同於剛纔幾個方法都是要替換into()方法的,listener()是結合into()方法一起使用的,當然也可以結合preload()方法一起使用。最基本的用法如下所示:

Glide.with(this)
     .load("http://www.guolin.tech/book.png")
     .listener(new RequestListener<Drawable>() {
         @Override
         public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
             return false;
         }

         @Override
         public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
             return false;
         }
     })
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

這裏我們在into()方法之前串接了一個listener()方法,然後實現了一個RequestListener的實例。其中RequestListener需要實現兩個方法,一個onResourceReady()方法,一個onLoadFailed()方法。從方法名上就可以看出來了,當圖片加載完成的時候就會回調onResourceReady()方法,而當圖片加載失敗的時候就會回調onLoadFailed()方法,onLoadFailed()方法中會將失敗的GlideException參數傳進來,這樣我們就可以定位具體失敗的原因了。

沒錯,listener()方法就是這麼簡單。不過還有一點需要處理,onResourceReady()方法和onLoadFailed()方法都有一個布爾值的返回值,返回false就表示這個事件沒有被處理,還會繼續向下傳遞,返回true就表示這個事件已經被處理掉了,從而不會再繼續向下傳遞。舉個簡單點的例子,如果我們在RequestListener的onResourceReady()方法中返回了true,那麼就不會再回調Target的onResourceReady()方法了。

關於回調與監聽的內容就講這麼多吧,如果想要學習更多深入的內容以及源碼解析,還是請參考這篇文章 Android圖片加載框架最全解析(四),玩轉Glide的回調與監聽 。

圖片變換

圖片變換的意思就是說,Glide從加載了原始圖片到最終展示給用戶之前,又進行了一些變換處理,從而能夠實現一些更加豐富的圖片效果,如圖片圓角化、圓形化、模糊化等等。

添加圖片變換的用法非常簡單,我們只需要在RequestOptions中串接transforms()方法,並將想要執行的圖片變換操作作爲參數傳入transforms()方法即可,如下所示:

RequestOptions options = new RequestOptions()
        .transforms(...);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

至於具體要進行什麼樣的圖片變換操作,這個通常都是需要我們自己來寫的。不過Glide已經內置了幾種圖片變換操作,我們可以直接拿來使用,比如CenterCrop、FitCenter、CircleCrop等。

但所有的內置圖片變換操作其實都不需要使用transform()方法,Glide爲了方便我們使用直接提供了現成的API:

RequestOptions options = new RequestOptions()
        .centerCrop();

RequestOptions options = new RequestOptions()
        .fitCenter();

RequestOptions options = new RequestOptions()
        .circleCrop();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

當然,這些內置的圖片變換API其實也只是對transform()方法進行了一層封裝而已,它們背後的源碼仍然還是藉助transform()方法來實現的。

這裏我們就選擇其中一種內置的圖片變換操作來演示一下吧,circleCrop()方法是用來對圖片進行圓形化裁剪的,我們動手試一下,代碼如下所示:

String url = "http://guolin.tech/book.png";
RequestOptions options = new RequestOptions()
        .circleCrop();
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

重新運行一下程序並點擊加載圖片按鈕,效果如下圖所示。

可以看到,現在展示的圖片是對原圖進行圓形化裁剪後得到的圖片。

當然,除了使用內置的圖片變換操作之外,我們完全可以自定義自己的圖片變換操作。理論上,在對圖片進行變換這個步驟中我們可以進行任何的操作,你想對圖片怎麼樣都可以。包括圓角化、圓形化、黑白化、模糊化等等,甚至你將原圖片完全替換成另外一張圖都是可以的。

不過由於這部分內容相對於Glide 3沒有任何的變化,因此就不再重複進行講解了。想學習自定義圖片變換操作的朋友們可以參考這篇文章 Android圖片加載框架最全解析(五),Glide強大的圖片變換功能 。

關於圖片變換,最後我們再來看一個非常優秀的開源庫,glide-transformations。它實現了很多通用的圖片變換效果,如裁剪變換、顏色變換、模糊變換等等,使得我們可以非常輕鬆地進行各種各樣的圖片變換。

glide-transformations的項目主頁地址是 https://github.com/wasabeef/glide-transformations 。

下面我們就來體驗一下這個庫的強大功能吧。首先需要將這個庫引入到我們的項目當中,在app/build.gradle文件當中添加如下依賴:

dependencies {
    implementation 'jp.wasabeef:glide-transformations:3.0.1'
}
  • 1
  • 2
  • 3

我們可以對圖片進行單個變換處理,也可以將多種圖片變換疊加在一起使用。比如我想同時對圖片進行模糊化和黑白化處理,就可以這麼寫:

String url = "http://guolin.tech/book.png";
RequestOptions options = new RequestOptions()
        .transforms(new BlurTransformation(), new GrayscaleTransformation());
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到,同時執行多種圖片變換的時候,只需要將它們都傳入到transforms()方法中即可。現在重新運行一下程序,效果如下圖所示。

當然,這只是glide-transformations庫的一小部分功能而已,更多的圖片變換效果你可以到它的GitHub項目主頁去學習。

自定義模塊

自定義模塊屬於Glide中的高級功能,同時也是難度比較高的一部分內容。

這裏我不可能在這一篇文章中將自定義模塊的內容全講一遍,限於篇幅的限制我只能講一講Glide 4中變化的這部分內容。關於Glide自定義模塊的全部內容,請大家去參考 Android圖片加載框架最全解析(六),探究Glide的自定義模塊功能 這篇文章。

自定義模塊功能可以將更改Glide配置,替換Glide組件等操作獨立出來,使得我們能輕鬆地對Glide的各種配置進行自定義,並且又和Glide的圖片加載邏輯沒有任何交集,這也是一種低耦合編程方式的體現。下面我們就來學習一下自定義模塊要如何實現。

首先定義一個我們自己的模塊類,並讓它繼承自AppGlideModule,如下所示:

@GlideModule
public class MyAppGlideModule extends AppGlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {

    }

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以看到,在MyAppGlideModule類當中,我們重寫了applyOptions()和registerComponents()方法,這兩個方法分別就是用來更改Glide配置以及替換Glide組件的。

注意在MyAppGlideModule類在上面,我們加入了一個@GlideModule的註解,這是Gilde 4和Glide 3最大的一個不同之處。在Glide 3中,我們定義了自定義模塊之後,還必須在AndroidManifest.xml文件中去註冊它才能生效,而在Glide 4中是不需要的,因爲@GlideModule這個註解已經能夠讓Glide識別到這個自定義模塊了。

這樣的話,我們就將Glide自定義模塊的功能完成了。後面只需要在applyOptions()和registerComponents()這兩個方法中加入具體的邏輯,就能實現更改Glide配置或者替換Glide組件的功能了。詳情還是請參考 Android圖片加載框架最全解析(六),探究Glide的自定義模塊功能 這篇文章,這裏就不再展開討論了。

使用Generated API

Generated API是Glide 4中全新引入的一個功能,它的工作原理是使用註解處理器 (Annotation Processor) 來生成出一個API,在Application模塊中可使用該流式API一次性調用到RequestBuilder,RequestOptions和集成庫中所有的選項。

這麼解釋有點拗口,簡單點說,就是Glide 4仍然給我們提供了一套和Glide 3一模一樣的流式API接口。畢竟有些人還是覺得Glide 3的API更好用一些,比如說我。

Generated API對於熟悉Glide 3的朋友來說那是再簡單不過了,基本上就是和Glide 3一模一樣的用法,只不過需要把Glide關鍵字替換成GlideApp關鍵字,如下所示:

GlideApp.with(this)
        .load(url)
        .placeholder(R.drawable.loading)
        .error(R.drawable.error)
        .skipMemoryCache(true)
        .diskCacheStrategy(DiskCacheStrategy.NONE)
        .override(Target.SIZE_ORIGINAL)
        .circleCrop()
        .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

不過,有可能你的IDE中會提供找不到GlideApp這個類。這個類是通過編譯時註解自動生成的,首先確保你的代碼中有一個自定義的模塊,並且給它加上了@GlideModule註解,也就是我們在上一節所講的內容。然後在Android Studio中點擊菜單欄Build -> Rebuild Project,GlideApp這個類就會自動生成了。

當然,Generated API所能做到的並不只是這些而已,它還可以對現有的API進行擴展,定製出任何屬於你自己的API。

下面我來具體舉個例子,比如說我們要求項目中所有圖片的緩存策略全部都要緩存原始圖片,那麼每次在使用Glide加載圖片的時候,都去指定diskCacheStrategy(DiskCacheStrategy.DATA)這麼長長的一串代碼,確實是讓人比較心煩。這種情況我們就可以去定製一個自己的API了。

定製自己的API需要藉助@GlideExtension和@GlideOption這兩個註解。創建一個我們自定義的擴展類,代碼如下所示:

@GlideExtension
public class MyGlideExtension {

    private MyGlideExtension() {

    }

    @GlideOption
    public static void cacheSource(RequestOptions options) {
        options.diskCacheStrategy(DiskCacheStrategy.DATA);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

這裏我們定義了一個MyGlideExtension類,並且給加上了一個@GlideExtension註解,然後要將這個類的構造函數聲明成private,這都是必須要求的寫法。

接下來就可以開始自定義API了,這裏我們定義了一個cacheSource()方法,表示只緩存原始圖片,並給這個方法加上了@GlideOption註解。注意自定義API的方法都必須是靜態方法,而且第一個參數必須是RequestOptions,後面你可以加入任意多個你想自定義的參數。

在cacheSource()方法中,我們仍然還是調用的diskCacheStrategy(DiskCacheStrategy.DATA)方法,所以說cacheSource()就是一層簡化API的封裝而已。

然後在Android Studio中點擊菜單欄Build -> Rebuild Project,神奇的事情就會發生了,你會發現你已經可以使用這樣的語句來加載圖片了:

GlideApp.with(this)
        .load(url)
        .cacheSource()
        .into(imageView);
  • 1
  • 2
  • 3
  • 4

有了這個強大的功能之後,我們使用Glide就能變得更加靈活了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章