1. 程式人生 > >Android圖片載入框架最全解析(五),Glide強大的圖片變換功能

Android圖片載入框架最全解析(五),Glide強大的圖片變換功能

                     
 

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

大家好,又到了學習Glide的時間了。前段時間由於專案開發緊張,再加上後來又生病了,所以停更了一個月,不過現在終於又可以恢復正常更新了。今天是這個系列的第五篇文章,在前面四篇文章的當中,我們已經學習了Glide的基本用法、Glide的工作原理和執行流程、Glide的快取機制、以及Glide的回撥機制等內容。如果你能將前面的四篇文章都掌握好了,那麼恭喜你,現在你已經是一名Glide好手了。

不過Glide的這個框架的功能實在是太強大了,它所能做的事情遠遠不止於目前我們所學的這些。因此,今天我們就再來學習一個新的功能模組,並且是一個非常重要的模組——Glide的圖片變化功能。

一個問題

在正式開始學習Glide的圖片變化功能之前,我們先來看一個問題,這個問題可能有不少人都在使用Glide的時候都遇到過,正好在本篇內容的主題之下我們順帶著將這個問題給解決了。

首先我們嘗試使用Glide來載入一張圖片,圖片URL地址是:

https://www.baidu.com/img/bd_logo1.png
  • 1

這是百度首頁logo的一張圖片,圖片尺寸是540*258畫素。

接下來我們編寫一個非常簡單的佈局檔案,如下所示:

<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="wrap_content"        android:layout_height="wrap_content"        />
</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

佈局檔案中只有一個按鈕和一個用於顯示圖片的ImageView。注意,ImageView的寬和高這裡設定的都是wrap_content。

然後編寫如下的程式碼來載入圖片:

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 = "https://www.baidu.com/img/bd_logo1.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
  • 18

這些簡單的程式碼對於現在的你而言應該都是小兒科了,相信我也不用再做什麼解釋。現在執行一下程式並點選載入圖片按鈕,效果如下圖所示。

圖片是正常加載出來了,不過大家有沒有發現一個問題。百度這張logo圖片的尺寸只有540*258畫素,但是我的手機的解析度卻是1080*1920畫素,而我們將ImageView的寬高設定的都是wrap_content,那麼圖片的寬度應該只有手機螢幕寬度的一半而已,但是這裡卻充滿了全屏,這是為什麼呢?

如果你之前也被這個問題困擾過,那麼恭喜,本篇文章正是你所需要的。之所以會出現這個現象,就是因為Glide的圖片變換功能所導致的。那麼接下來我們會先分析如何解決這個問題,然後再深入學習Glide圖片變化的更多功能。

稍微對Android有點了解的人應該都知道ImageView有scaleType這個屬性,但是可能大多數人卻不知道,如果在沒有指定scaleType屬性的情況下,ImageView預設的scaleType是什麼?

這個問題如果直接問我,我也答不上來。不過動手才是檢驗真理的唯一標準,想知道答案,自己動手試一下就知道了。

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    ImageView imageView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        imageView = (ImageView) findViewById(R.id.image_view);        Log.d(TAG, "imageView scaleType is " + imageView.getScaleType());    }    ...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可以看到,我們在onCreate()方法中列印了ImageView預設的scaleType,然後重新執行一下程式,結果如下圖所示:

由此我們可以得知,在沒有明確指定的情況下,ImageView預設的scaleType是FIT_CENTER。

有了這個前提條件,我們就可以繼續去分析Glide的原始碼了。當然,本文中的原始碼還是建在第二篇原始碼分析的基礎之上,還沒有看過這篇文章的朋友,建議先去閱讀 Android圖片載入框架最全解析(二),從原始碼的角度理解Glide的執行流程

回顧一下第二篇文章中我們分析過的into()方法,它是在GenericRequestBuilder類當中的,程式碼如下所示:

public Target<TranscodeType> into(ImageView view) {    Util.assertMainThread();    if (view == null) {        throw new IllegalArgumentException("You must pass in a non null View");    }    if (!isTransformationSet && view.getScaleType() != null) {        switch (view.getScaleType()) {            case CENTER_CROP:                applyCenterCrop();                break;            case FIT_CENTER:            case FIT_START:            case FIT_END:                applyFitCenter();                break;            //$CASES-OMITTED$            default:                // Do nothing.        }    }    return into(glide.buildImageViewTarget(view, transcodeClass));}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

還記得我們當初分析這段程式碼的時候,直接跳過前面的所有程式碼,直奔最後一行。因為那個時候我們的主要任務是分析Glide的主線執行流程,而不去仔細閱讀它的細節,但是現在我們是時候應該閱讀一下細節了。

可以看到,這裡在第7行會進行一個switch判斷,如果ImageView的scaleType是CENTER_CROP,則會去呼叫applyCenterCrop()方法,如果scaleType是FIT_CENTER、FIT_START或FIT_END,則會去呼叫applyFitCenter()方法。這裡的applyCenterCrop()和applyFitCenter()方法其實就是向Glide的載入流程中添加了一個圖片變換操作,具體的原始碼我們就不跟進去看了。

那麼現在我們就基本清楚了,由於ImageView預設的scaleType是FIT_CENTER,因此會自動新增一個FitCenter的圖片變換,而在這個圖片變換過程中做了某些操作,導致圖片充滿了全屏。

那麼我們該如何解決這個問題呢?最直白的一種辦法就是看著原始碼來改。當ImageView的scaleType是CENTER_CROP、FIT_CENTER、FIT_START或FIT_END時不是會自動新增一個圖片變換操作嗎?那我們把scaleType改成其他值不就可以了。ImageView的scaleType可選值還有CENTER、CENTER_INSIDE、FIT_XY等。這當然是一種解決方案,不過只能說是一種比較笨的解決方案,因為我們為了解決這個問題而去改動了ImageView原有的scaleType,那如果你真的需要ImageView的scaleType為CENTER_CROP或FIT_CENTER時可能就傻眼了。

上面只是我們通過分析原始碼得到的一種解決方案,並不推薦大家使用。實際上,Glide給我們提供了專門的API來新增和取消圖片變換,想要解決這個問題只需要使用如下程式碼即可:

Glide.with(this)     .load(url)     .dontTransform()     .into(imageView);
  • 1
  • 2
  • 3
  • 4

可以看到,這裡呼叫了一個dontTransform()方法,表示讓Glide在載入圖片的過程中不進行圖片變換,這樣剛才呼叫的applyCenterCrop()、applyFitCenter()就統統無效了。

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

這樣圖片就只會佔據半個螢幕的寬度了,說明我們的程式碼奏效了。

但是使用dontTransform()方法存在著一個問題,就是呼叫這個方法之後,所有的圖片變換操作就全部失效了,那如果我有一些圖片變換操作是必須要執行的該怎麼辦呢?不用擔心,總歸是有辦法的,這種情況下我們只需要藉助override()方法強制將圖片尺寸指定成原始大小就可以了,程式碼如下所示:

Glide.with(this)     .load(url)     .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)     .into(imageView);
  • 1
  • 2
  • 3
  • 4

通過override()方法將圖片的寬和高都指定成Target.SIZE_ORIGINAL,問題同樣被解決了。程式的最終執行結果和上圖是完全一樣的,我就不再重新截圖了。

由此我們可以看出,之所以會出現這個問題,和Glide的圖片變換功能是撇不開關係的。那麼也是通過這個問題,我們對Glide的圖片變換有了一個最基本的認識。接下來,就讓我們正式開始進入本篇文章的正題吧。

圖片變換的基本用法

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

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

Glide.with(this)     .load(url)     .transform(...)     .into(imageView);
  • 1
  • 2
  • 3
  • 4

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

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

Glide.with(this)     .load(url)     .centerCrop()     .into(imageView);Glide.with(this)     .load(url)     .fitCenter()     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

當然,centerCrop()和fitCenter()方法其實也只是對transform()方法進行了一層封裝而已,它們背後的原始碼仍然還是藉助transform()方法來實現的,如下所示:

public class DrawableRequestBuilder<ModelType>        extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>        implements BitmapOptions, DrawableOptions {    ...    /**     * Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.     *     * @see #fitCenter()     * @see #transform(BitmapTransformation...)     * @see #bitmapTransform(Transformation[])     * @see #transform(Transformation[])     *     * @return This request builder.     */    @SuppressWarnings("unchecked")    public DrawableRequestBuilder<ModelType> centerCrop() {        return transform(glide.getDrawableCenterCrop());    }    /**     * Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.     *     * @see #centerCrop()     * @see #transform(BitmapTransformation...)     * @see #bitmapTransform(Transformation[])     * @see #transform(Transformation[])     *     * @return This request builder.     */    @SuppressWarnings("unchecked")    public DrawableRequestBuilder<ModelType> fitCenter() {        return transform(glide.getDrawableFitCenter());    }    ...}
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

那麼這兩種內建的圖片變換操作到底能實現什麼樣的效果呢?FitCenter的效果其實剛才我們已經見識過了,就是會將圖片按照原始的長寬比充滿全屏。那麼CenterCrop又是什麼樣的效果呢?我們來動手試一下就知道了。

為了讓效果更加明顯,這裡我就不使用百度首頁的Logo圖了,而是換成必應首頁的一張美圖。在不應用任何圖片變換的情況下,使用Glide載入必應這張圖片效果如下所示。

現在我們新增一個CenterCrop的圖片變換操作,程式碼如下:

String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";Glide.with(this)     .load(url)     .centerCrop()     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5

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

可以看到,現在展示的圖片是對原圖的中心區域進行裁剪後得到的圖片。

另外,centerCrop()方法還可以配合override()方法來實現更加豐富的效果,比如指定圖片裁剪的比例:

String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";Glide.with(this)     .load(url)     .override(500, 500)     .centerCrop()     .into(imageView);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以看到,這裡我們將圖片的尺寸設定為500*500畫素,那麼裁剪的比例也就變成1:1了,現在重新執行一下程式,效果如下圖所示。

這樣我們就把Glide內建的圖片變換介面的用法都掌握了。不過不得不說,Glide內建的圖片變換介面功能十分單一且有限,完全沒有辦法滿足我們平時的開發需求。因此,掌握自定義圖片變換功能就顯得尤為重要了。

不過,在正式開始學習自定義圖片變換功能之前,我們先來探究一下CenterCrop這種圖片變換的原始碼,理解了它的原始碼我們再來進行自定義圖片變換就能更加得心應手了。

原始碼分析

那麼就話不多說,我們直接開啟CenterCrop類來看一下它的原始碼吧,如下所示:

public class CenterCrop extends BitmapTransformation {    public CenterCrop(Context context) {        super(context);    }    public CenterCrop(BitmapPool bitmapPool) {        super(bitmapPool);    }    // Bitmap doesn't implement equals, so == and .equals are equivalent here.    @SuppressWarnings("PMD.CompareObjectsWithEquals")    @Override    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {        final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null                ? toTransform.getConfig() : Bitmap.Config.ARGB_8888);        Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);        if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {            toReuse.recycle();        }        return transformed;    }    @Override    public String getId() {        return "CenterCrop.com.bumptech.glide.load.resource.bitmap";    }}
  • 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
  • 28

這段程式碼並不長,但是我還是要劃下重點,這樣大家看起來的時候會更加輕鬆。

首先,CenterCrop是繼承自BitmapTransformation的,這個是重中之重,因為整個圖片變換功能都是建立在這個繼承結構基礎上的。

接下來CenterCrop中最重要的就是transform()方法,其他的方法我們可以暫時忽略。transform()方法中有四個引數,每一個都很重要,我們來一一解讀下。第一個引數pool,這個是Glide中的一個Bitmap快取池,用於對Bitmap物件進行重用,否則每次圖片變換都重新建立Bitmap物件將會非常消耗記憶體。第二個引數toTransform,這個是原始圖片的Bitmap物件,我們就是要對它來進行圖片變換。第三和第四個引數比較簡單,分別代表圖片變換後的寬度和高度,其實也就是override()方法中傳入的寬和高的值了。

下面我們來看一下transform()方法的細節,首先第一行就從Bitmap快取池中嘗試獲取一個可重用的Bitmap物件,然後把這個物件連同toTransform、outWidth、outHeight引數一起傳入到了TransformationUtils.centerCrop()方法當中。那麼我們就跟進去來看一下這個方法的原始碼,如下所示:

public final class TransformationUtils {    ...    public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {        if (toCrop == null) {            return null;        } else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {            return toCrop;        }        // From ImageView/Bitmap.createScaledBitmap.        final float scale;        float dx = 0, dy = 0;        Matrix m = new Matrix();        if (toCrop.getWidth() * height > width * toCrop.getHeight()) {            scale = (float) height / (float) toCrop.getHeight();            dx = (width - toCrop.getWidth() * scale) * 0.5f;        } else {            scale = (float) width / (float) toCrop.getWidth();            dy = (height - toCrop.getHeight() * scale) * 0.5f;        }        m.setScale(scale, scale);        m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));        final Bitmap result;        if (recycled != null) {            result = recycled;        } else {            result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));        }        // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.        TransformationUtils.setAlpha(toCrop, result);        Canvas canvas = new Canvas(result);        Paint paint = new Paint(PAINT_FLAGS);        canvas.drawBitmap(toCrop, m, paint);        return result;    }    ...}
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

這段程式碼就是整個圖片變換功能的核心程式碼了。可以看到,第5-9行主要是先做了一些校驗,如果原圖為空,或者原圖的尺寸和目標裁剪尺寸相同,那麼就放棄裁剪。接下來第11-22行是通過數學計算來算出畫布的縮放的比例以及偏移值。第24-29行是判斷快取池中取出的Bitmap物件是否為空,如果不為空就可以直接使用,如果為空則要建立一個新的Bitmap物件。第32行是將原圖Bitmap物件的alpha值複製到裁剪Bitmap物件上面。最後第34-37行是裁剪Bitmap物件進行繪製,並將最終的結果進行返回。全部的邏輯就是這樣,總體來說還是比較簡單的,可能也就是數學計算那邊需要稍微動下腦筋。

那麼現在得到了裁剪後的Bitmap物件,我們再回到CenterCrop當中,你會看到,在最終返回這個Bitmap物件之前,還會嘗試將複用的Bitmap物件重新放回到快取池當中,以便下次繼續使用。

好的,這樣我們就將CenterCrop圖片變換的工作原理完整地分析了一遍,FitCenter的原始碼也是基本類似的,這裡就不再重複分析了。瞭解了這些內容之後,接下來我們就可以開始學習自定義圖片變換功能了。

自定義圖片變換

Glide給我們定製好了一個圖片變換的框架,大致的流程是我們可以獲取到原始的圖片,然後對圖片進行變換,再將變換完成後的圖片返回給Glide,最終由Glide將圖片顯示出來。理論上,在對圖片進行變換這個步驟中我們可以進行任何的操作,你想對圖片怎麼樣都可以。包括圓角化、圓形化、黑白化、模糊化等等,甚至你將原圖片完全替換成另外一張圖都是可以的。

但是這裡顯然我不可能向大家演示所有圖片變換的可能,圖片變換的可能性也是無限的。因此這裡我們就選擇一種常用的圖片變換效果來進行自定義吧——對圖片進行圓形化變換。

圖片圓形化的功能現在在手機應用中非常常見,比如手機QQ就會將使用者的頭像進行圓形化變換,從而使得介面變得更加好看。

自定義圖片變換功能的實現邏輯比較固定,我們剛才看過CenterCrop的原始碼之後,相信你已經基本瞭解整個自定義的過程了。其實就是自定義一個類讓它繼承自BitmapTransformation ,然後重寫transform()方法,並在這裡去實現具體的圖片變換邏輯就可以了。一個空的圖片變換實現大概如下所示:

public class CircleCrop extends BitmapTransformation {    public CircleCrop(Context context) {        super(context);    }    public CircleCrop(BitmapPool bitmapPool) {        super(bitmapPool);    }    @Override    public String getId() {        return "com.example.glidetest.CircleCrop";    }    @Override    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {        return null;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

這裡有一點需要注意,就是getId()方法中要求返回一個唯一的字串來作為id,以和其他的圖片變換做區分。通常情況下,我們直接返回當前類的完整類名就可以了。

另外,這裡我們選擇繼承BitmapTransformation還有一個限制,就是隻能對靜態圖進行圖片變換。當然,這已經足夠覆蓋日常95%以上的開發需求了。如果你有特殊的需求要對GIF圖進行圖片變換,那就得去自己實現Transformation接口才可以了。不過這個就非常複雜了,不在我們今天的討論範圍。

好了,那麼我們繼續實現對圖片進行圓形化變換的功能,接下來只需要在transform()方法中去做具體的邏輯實現就可以了,程式碼如下所示:

public class CircleCrop extends BitmapTransformation {    public CircleCrop(Context context) {        super(context);    }    public CircleCrop(BitmapPool bitmapPool) {        super(bitmapPool);    }    @Override    public String getId() {        return "com.example.glidetest.CircleCrop";    }    @Override    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {        int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight());        final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);        final Bitmap result;        if (toReuse != null) {            result = toReuse;        } else {            result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);        }        int dx = (toTransform.getWidth() - diameter) / 2;        int dy = (toTransform.getHeight() - diameter) / 2;        Canvas canvas = new Canvas(result);        Paint paint = new Paint();        BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP,                                             BitmapShader.TileMode.CLAMP);        if (dx != 0 || dy != 0) {            Matrix matrix = new Matrix();            matrix.setTranslate(-dx, -dy);            shader.setLocalMatrix(matrix);        }        paint.setShader(shader);        paint.setAntiAlias(true);        float radius = diameter / 2f;        canvas.drawCircle(radius, radius, radius, paint);        if (toReuse != null && !pool.put(toReuse)) {            toReuse.recycle();        }        return result;    }}
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

下面我來對transform()方法中的邏輯做下簡單的解釋。首先第18行先算出原圖寬度和高度中較小的值,因為對圖片進行圓形化變換肯定要以較小的那個值作為直徑來進行裁剪。第20-26行則和剛才一樣,從Bitmap快取池中嘗試獲取一個Bitmap物件來進行重用,如果沒有可重用的Bitmap物件的話就建立一個。第28-41行是具體進行圓形化變換的部分,這裡算出了畫布的偏移值,並且根據剛才得到的直徑算出半徑來進行畫圓。最後,嘗試將複用的Bitmap物件重新放回到快取池當中,並將圓形化變換後的Bitmap物件進行返回。

這樣,一個自定義圖片變換的功能就寫好了,那麼現在我們就來嘗試使用一下它吧。使用方法非常簡單,剛才已經介紹過了,就是把這個自定義圖片變換的例項傳入到transform()方法中即可,如下所示:

Glide.with(this)     .load(url)     .transform(new CircleCrop(this))     .into(imageView);
  • 1
  • 2
  • 3
  • 4

現在我們重新執行一下程式,效果如下圖所示。

更多圖片變換功能

雖說Glide的圖片變換功能框架已經很強大了,使得我們可以輕鬆地自定義圖片變換效果,但是如果每一種圖片變換都要我們自己去寫還是蠻吃力的。事實上,確實也沒有必要完全靠自己去實現各種各樣的圖片變換效果,因為大多數的圖片變換都是比較通用的,各個專案會用到的效果都差不多,我們每一個都自己去重新實現無異於重複造輪子。

也正是因此,網上出現了很多Glide的圖片變換開源庫,其中做的最出色的應該要數glide-transformations這個庫了。它實現了很多通用的圖片變換效果,如裁剪變換、顏色變換、模糊變換等等,使得我們可以非常輕鬆地進行各種各樣的圖片變換。

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

dependencies {    compile 'jp.wasabeef:glide-transformations:2.0.2'}
  • 1
  • 2
  • 3

現在如果我想對圖片進行模糊化處理,那麼就可以使用glide-transformations庫中的BlurTransformation這個類,程式碼如下所示:

Glide.with(this)     .load(url)     .bitmapTransform(new BlurTransformation(this))     .into(imageView);
  • 1
  • 2
  • 3
  • 4

注意這裡我們呼叫的是bitmapTransform()方法而不是transform()方法,因為glide-transformations庫都是專門針對靜態圖片變換來進行設計的。現在重新執行一下程度,效果如下圖所示。

沒錯,我們就這樣輕鬆地實現模糊化的效果了。

接下來我們再試一下圖片黑白化的效果,使用的是GrayscaleTransformation這個類,程式碼如下所示:

Glide.with(this)     .load(url)     .bitmapTransform(new GrayscaleTransformation(this))     .into(imageView);
  • 1
  • 2
  • 3
  • 4

現在重新執行一下程度,效果如下圖所示。

而且我們還可以將多個圖片變換效果組合在一起使用,比如同時執行模糊化和黑白化的變換:

Glide.with(this)     .load(url)     .bitmapTransform(new BlurTransformation(this), new GrayscaleTransformation(this))     .into(imageView);
  • 1
  • 2
  • 3
  • 4

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

當然,這些只是glide-transformations庫的一小部分功能而已,更多的圖片變換效果你可以到它的GitHub專案主頁去學習,所有變換的用法都是這麼簡單哦。

好了,那麼今天的文章就到這裡了,相信大家的收穫都很多吧。下篇文章中我們會繼續深入探究Glide,學習一下自定義模組的功能,感興趣的朋友請繼續閱讀 Android圖片載入框架最全解析(六),探究Glide的自定義模組功能

 

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

   

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