Glide 系列(七) Glide圖像變換

前言

通過(guò)前面Glide系列文章的閱讀,相信大家對(duì)Glide的核心流程及部分關(guān)鍵模塊已經(jīng)有了較為深入的了解医舆,本節(jié)我們繼續(xù)深入介紹Glide中的重要模塊嘉汰。在平時(shí)的開(kāi)發(fā)需求中皿曲,有時(shí)需要對(duì)原圖片進(jìn)行一定的處理后再顯示出來(lái),以產(chǎn)生更加豐富的展示效果粤铭,比如對(duì)圖片進(jìn)行裁剪挖胃,高斯模糊,圓角處理梆惯,美化處理等等酱鸭,如何添加這些個(gè)性化的圖片需求,Glide框架提供了相關(guān)的接口加袋,可以輕松接入我們需要的圖片變換功能凛辣。

流程回顧

在講這部分前,先思考一下职烧,如果我們需要對(duì)圖片進(jìn)行顯示效果的處理扁誓,首先應(yīng)該拿到原圖片,對(duì)圖片進(jìn)行相應(yīng)的處理操作后將結(jié)果返回給后續(xù)流程蚀之,因此圖片變換的處理時(shí)機(jī)應(yīng)在對(duì)數(shù)據(jù)流解碼之后蝗敢,回顧一下前面章節(jié)Glide圖片加載的部分流程,如下圖所示


由流程圖中可以看到足删,DecodeJob 在發(fā)起網(wǎng)絡(luò)請(qǐng)求獲取到數(shù)據(jù)流之后寿谴,會(huì)對(duì)數(shù)據(jù)流進(jìn)行解碼處理得到bitmap數(shù)據(jù),然后調(diào)用到transformEncodeAndTranscode方法失受,這個(gè)方法中先對(duì)圖像進(jìn)行變換處理讶泰,使用到圖像變換器transformation中的transform方法咏瑟,在這里面完成對(duì)圖像相應(yīng)的處理流程,看下相關(guān)源碼痪署。

    private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
        long startTime = LogTime.getLogTime();
        Resource<T> transformed = transform(decoded); // 調(diào)用變換方法码泞,對(duì)圖像進(jìn)行處理
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transformed resource from source", startTime);
        }

        writeTransformedToCache(transformed);

        startTime = LogTime.getLogTime();
        Resource<Z> result = transcode(transformed);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transcoded transformed from source", startTime);
        }
        return result;
    }

基本使用

那這個(gè)圖像變換器transformation是如何設(shè)置的呢,設(shè)置方法非常簡(jiǎn)單如下代碼所示狼犯,只需要調(diào)用transform方法傳入所需要的圖像變換器即可余寥,可以根據(jù)需要傳入一個(gè)或多個(gè)圖像變換器:

Glide.with(this)
     .load(url)
     .transform(...) // 設(shè)置圖像變換器
     .into(imageView);

參考前面對(duì)Glide核心流程的源碼分析,調(diào)用load方法之后會(huì)返回一個(gè)DrawableTypeRequest類型的對(duì)象悯森,transform最終調(diào)用到其父類GenericRequestBuilder 中的transform方法宋舷,用于設(shè)置圖像變換器參數(shù),并將其封裝到GenericRequest中瓢姻, 供圖像解碼之后的流程使用祝蝠,如下部分源碼所示:

    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
            Transformation<ResourceType>... transformations) {
        isTransformationSet = true;
        if (transformations.length == 1) {
            transformation = transformations[0]; // 保存設(shè)置的圖像變換參數(shù)
        } else {
            transformation = new MultiTransformation<ResourceType>(transformations);
        }
        return this;
    }

    private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
            RequestCoordinator requestCoordinator) {
        return GenericRequest.obtain(
                loadProvider,
                model,
                signature,
                context,
                priority,
                target,
                sizeMultiplier,
                placeholderDrawable,
                placeholderId,
                errorPlaceholder,
                errorId,
                fallbackDrawable,
                fallbackResource,
                requestListener,
                requestCoordinator,
                glide.getEngine(),
                transformation,
                transcodeClass,
                isCacheable,
                animationFactory,
                overrideWidth,
                overrideHeight,
                diskCacheStrategy);
    }

自定義圖像變換

可以看到圖像變換器的設(shè)置方法是非常簡(jiǎn)單的,圖像變換最關(guān)鍵的是處理算法的實(shí)現(xiàn)汹来,如何來(lái)實(shí)現(xiàn)一個(gè)具體的圖像變換功能呢续膳,Glide中給我們提供了幾個(gè)現(xiàn)成的圖片變換功能,我們以Glide中自帶的圖形變換功能 CenterCrop作為例子來(lái)看如何實(shí)現(xiàn)一個(gè)圖像變換器收班,并應(yīng)用到圖像加載中坟岔。CenterCrop的源碼如下所示:

public class CenterCrop extends BitmapTransformation {

    public CenterCrop(Context context) {
        super(context);
    }

    public CenterCrop(BitmapPool bitmapPool) {
        super(bitmapPool);
    }
    
    @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); // 獲取可復(fù)用的bitmap對(duì)象
        Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); // 對(duì)原始bitmap進(jìn)行圖像變換處理
        if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
            toReuse.recycle();
        }
        return transformed;
    }

    @Override
    public String getId() {
        return "CenterCrop.com.bumptech.glide.load.resource.bitmap"; // 圖像變換的唯一標(biāo)識(shí)
    }
}

centercrop是一種圖像裁剪算法,等比例縮放原圖直到填滿ImageView為止摔桦,對(duì)多出來(lái)的部分進(jìn)行裁剪社付。ImageView 完全填充,圖像可能會(huì)被裁減掉一部分邻耕。Glide 中的CenterCrop 繼承自 BitmapTransformation 類 (靜態(tài)圖像的變換類型都是繼承BitmapTransformation)鸥咖。 主要實(shí)現(xiàn)了transform 方法,在transform方法中完成對(duì)圖像裁剪的計(jì)算和處理兄世。另外還實(shí)現(xiàn)了getId方法啼辣,用來(lái)區(qū)分不同的變換類型。我們繼續(xù)看transform方法御滩,transform方法中共傳入四個(gè)參數(shù)鸥拧,分別是用于bitmap復(fù)用的bitmapool,圖像變換前的原圖 toTransform, 指定的變換后的圖像寬高(可以通過(guò)override方法來(lái)設(shè)置)。在centercrop變換中削解,首先從bitmappool中取出一個(gè)可復(fù)用的bitmap富弦,用于保存變換后的bitmap,(這里穿插一句氛驮,為了防止頻繁創(chuàng)建bitmap對(duì)象而產(chǎn)生大量的內(nèi)存消耗腕柜,Glide中使用 bitmappool來(lái)對(duì)bitmap進(jìn)行復(fù)用,在平時(shí)對(duì)bitmap的使用中,從內(nèi)存優(yōu)化的角度我們可以借鑒這一做法)盏缤,然后將這些參數(shù)傳入TransformationUtils.centerCrop 方法砰蠢,在這里面根據(jù)設(shè)置的尺寸及圖像原始寬高進(jìn)行計(jì)算,完成的圖像裁切功能蛾找,如下源碼所示:

    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;
        }
        final float scale;
        float dx = 0, dy = 0;
        Matrix m = new Matrix();
        // 根據(jù)原圖尺寸及所需要展示的尺寸進(jìn)行裁剪位置計(jì)算
        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));
        }

        TransformationUtils.setAlpha(toCrop, result);

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint(PAINT_FLAGS);
        canvas.drawBitmap(toCrop, m, paint);
        return result;
    }

圖像變換開(kāi)源庫(kù)

由此可見(jiàn)赵誓,實(shí)現(xiàn)一個(gè)圖像變換器打毛,只需要繼承BitmapTransformation 類 (靜態(tài)圖像的變換類型都是繼承BitmapTransformation)。 實(shí)現(xiàn)transform 方法和getid方法俩功。圖片變換的需求多種多樣幻枉,如果我們想要豐富的圖像展示效果,也不必全部自己來(lái)研究圖像處理算法實(shí)現(xiàn)诡蜓。目前已經(jīng)有非常多Glide圖片變換的開(kāi)源庫(kù)可以直接使用熬甫,比較全面的是glide-transformations這個(gè)庫(kù),實(shí)現(xiàn)了非常豐富的圖片變換效果蔓罚,例如裁剪變換椿肩、顏色變換、模糊變換等等豺谈,glide-transformations的GitHub地址是 https://github.com/wasabeef/glide-transformations 郑象。這可以看一下部分效果:

圖像變換效果展示

這個(gè)庫(kù)的使用也非常簡(jiǎn)單,在app/build.gradle文件當(dāng)中添加如下依賴:

dependencies {
    compile 'jp.wasabeef:glide-transformations:2.0.2'
}

然后就可以開(kāi)心的玩耍了茬末,例如對(duì)圖像進(jìn)行模糊處理只需如下代碼即可:

Glide.with(this)
     .load(url)
     .transform(new BlurTransformation(this,20))
     .into(imageView);

使用圖像顏色濾鏡效果:

Glide.with(this)
     .load(url)
     .transform(new ColorFilterTransformation(this, 0x7900CCCC))
     .into(imageView);

至此對(duì)Glide圖像變換這一部分的內(nèi)容就講解完了厂榛,對(duì)自定義圖像變換模塊以及如何使用圖像變換功能進(jìn)行了詳細(xì)的介紹。后續(xù)我們會(huì)繼續(xù)深入介紹Glide關(guān)鍵模塊知識(shí)丽惭,歡迎繼續(xù)關(guān)注击奶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市责掏,隨后出現(xiàn)的幾起案子柜砾,更是在濱河造成了極大的恐慌,老刑警劉巖换衬,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痰驱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡冗疮,警方通過(guò)查閱死者的電腦和手機(jī)萄唇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)术幔,“玉大人另萤,你說(shuō)我怎么就攤上這事。” “怎么了四敞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵泛源,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我忿危,道長(zhǎng)达箍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任铺厨,我火速辦了婚禮缎玫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘解滓。我一直安慰自己赃磨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布洼裤。 她就那樣靜靜地躺著邻辉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腮鞍。 梳的紋絲不亂的頭發(fā)上值骇,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音移国,去河邊找鬼吱瘩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛桥狡,可吹牛的內(nèi)容都是我干的搅裙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼裹芝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼部逮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起嫂易,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兄朋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后怜械,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體颅和,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年缕允,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了峡扩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡障本,死狀恐怖教届,靈堂內(nèi)的尸體忽然破棺而出响鹃,到底是詐尸還是另有隱情,我是刑警寧澤案训,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布买置,位于F島的核電站,受9級(jí)特大地震影響强霎,放射性物質(zhì)發(fā)生泄漏忿项。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一城舞、第九天 我趴在偏房一處隱蔽的房頂上張望轩触。 院中可真熱鬧,春花似錦椿争、人聲如沸怕膛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掸茅,卻和暖如春椅邓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昧狮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工景馁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逗鸣。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓合住,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親撒璧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子透葛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353