1. 程式人生 > >Android圖片載入框架最全解析(八),帶你全面瞭解Glide 4的用法

Android圖片載入框架最全解析(八),帶你全面瞭解Glide 4的用法

本文同步發表於我的微信公眾號,掃一掃文章底部的二維碼或在微信搜尋 郭霖 即可關注,每天都有文章更新。

本篇將是我們這個Glide系列的最後一篇文章。

其實在寫這個系列第一篇文章的時候,Glide就推出4.0.0的RC版了。那個時候因為我一直研究的都是Glide 3.7.0版本,再加上RC版本還不太穩定,因此整個系列也都是基於3.7.0版本來寫的。

而現在,Glide的最新版本已經出到了4.4.0,可以說Glide 4已經是相當成熟和穩定了。而且也不斷有朋友一直在留言,想讓我講一講Glide 4的用法,因為Glide 4相對於Glide 3改動貌似還是挺大的,學完了Glide 3再去使用Glide 4,發現根本就無法使用。

OK,那麼今天就讓我們用《帶你全面瞭解Glide 4的用法》這樣一篇文章,給這個Glide系列畫上一個圓滿的句號。

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'
}

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

另外,Glide中需要用到網路功能,因此你還得在AndroidManifest.xml中宣告一下網路許可權才行:

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

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

載入圖片

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

http://guolin.tech/book.png

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

那麼首先開啟專案的佈局檔案,在佈局當中加入一個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>

為了讓使用者點選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);
    }

}

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

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

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

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

仍然還是傳統的三步走:先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);

沒錯,就是這麼簡單。這裡我們先建立了一個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);

可以看到,這裡在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);

很簡單,這裡又串接了一個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);
    }

}

指定圖片大小

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

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

RequestOptions options = new RequestOptions()
        .override(200, 100);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

仍然非常簡單,這裡使用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);

這樣的話,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);

可以看到,只需要呼叫skipMemoryCache()方法並傳入true,就表示禁用掉Glide的記憶體快取功能。

接下來我們開始學習硬碟快取方面的內容。

其實在剛剛學習佔位圖功能的時候,我們就使用過硬碟快取的功能了。當時為了禁止Glide對圖片進行硬碟快取而使用瞭如下程式碼:

RequestOptions options = new RequestOptions()
        .diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

呼叫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其中一個非常亮眼的功能就是可以載入GIF圖片,而同樣作為非常出色的圖片載入框架的Picasso是不支援這個功能的。

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

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

現在重新執行一下程式碼,效果如下圖所示:

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

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

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

Glide.with(this)
     .asBitmap()
     .load("http://guolin.tech/test.gif")
     .into(imageView);

可以看到,這裡在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);
}

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

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

2. preload()方法

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

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

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

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

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

呼叫了預載入之後,我們以後想再去載入這張圖片就會非常快了,因為Glide會直接從快取當中去讀取圖片並顯示出來,程式碼如下所示:

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

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();
}

這段程式碼稍微有一點點長,我帶著大家解讀一下。首先,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);

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

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

圖片變換

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

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

RequestOptions options = new RequestOptions()
        .transforms(...);
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

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

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

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

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

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

當然,這些內建的圖片變換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);

重新執行一下程式並點選載入圖片按鈕,效果如下圖所示。

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

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

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

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

下面我們就來體驗一下這個庫的強大功能吧。首先需要將這個庫引入到我們的專案當中,在app/build.gradle檔案當中新增如下依賴:

dependencies {
    implementation 'jp.wasabeef:glide-transformations:3.0.1'
}

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

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);

可以看到,同時執行多種圖片變換的時候,只需要將它們都傳入到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) {

    }

}

可以看到,在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);

不過,有可能你的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);
    }

}

這裡我們定義了一個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);

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

結束語

這樣我們基本上就將Glide 4的所有重要內容都介紹完了,如果你以前非常熟悉Glide 3的話,看完這篇文章之後相信你已經能夠熟練使用Glide 4了。而如果你以前並未接觸過Glide,僅僅只看這一篇文章可能瞭解得還不夠深入,建議最好還是把前面的七篇文章也去通讀一下,這樣你才能成為一名Glide好手。

我翻了一下歷史記錄,在今年的3月21號發了這個系列的第一篇文章,用了10個月的時間終於把這個系列全部更新完了。當時承諾的是寫八篇文章,如今兌現了承諾,也算是有始有終吧。未來我希望能繼續給大家帶來更好的技術文章,不過這個系列就到此為止了。也感謝有耐心的朋友能夠看到最後,能堅持看完的人,你們都和我一樣棒。

關注我的技術公眾號,每天都有優質技術文章推送。關注我的娛樂公眾號,工作、學習累了的時候放鬆一下自己。

微信掃一掃下方二維碼即可關注: