Glide坑遇記:寬度鋪滿高度自適應(yīng) & GIF加載之坑

Glide坑遇記

Glide 坑遇記

有一段時(shí)間沒有更新文章了,但登錄 簡書 發(fā)現(xiàn)時(shí)不時(shí)也有新的點(diǎn)贊和關(guān)注,在這里十分感謝大家的認(rèn)可,也為這段時(shí)間的靜默表示抱歉。這段時(shí)間確實(shí)有點(diǎn)忙,自己一直在利用 一些平臺(這里不說出平臺名稱了,一來避嫌,二來現(xiàn)在對這些平臺提供的數(shù)據(jù)也沒太多好感了) 提供的接口,敲寫一個(gè)生活工具類的App,一方面是使用 Retrofit + RxJava + okhttp 這種比較熱門的框架結(jié)構(gòu)熟悉一下,一方面是集體測試一下 GitHub 平時(shí)不太使用的熱門庫?,F(xiàn)在這個(gè)項(xiàng)目還在開發(fā)中,上線后會(huì)通知大家,也會(huì)在適當(dāng)?shù)臅r(shí)候把這個(gè)項(xiàng)目的源代碼開源到 GitHub,同時(shí)也會(huì)寫一些這個(gè)項(xiàng)目用到的大家感興趣的技術(shù)的介紹文章。好了,啰嗦的夠多了,開始今天的正題...

Glide 的基本使用可以查看下面這些文章:

圖片加載庫Glide介紹
Glide圖片加載庫的使用

1、Glide 實(shí)現(xiàn) ImageView 寬度填滿,高度自適應(yīng)的效果

要說這個(gè),就要先說一下大家在平時(shí)用到 ImageView 實(shí)現(xiàn)寬度填滿,高度自適應(yīng)的方法。

ImageView 寬度填滿,高度自適應(yīng)常用在:

  1. ListView 列表布局的條目中(RecycleView 同理),比如實(shí)現(xiàn) item 中的圖片充滿屏幕,高度根據(jù)具體圖片比例自適應(yīng),商品詳情中常常用到。
  2. GridView 網(wǎng)格布局的條目中,假如 item 有兩列,想讓每一列的 item 中的圖片占用屏幕的一半。
  3. 其他使用單獨(dú)圖片也想達(dá)到這種效果的場景。

這里提供兩種實(shí)現(xiàn)方法,也都是大家都知道:

1、重寫 onMeasure 方法

public class ResizableImageView extends ImageView {  
  
    public ResizableImageView(Context context) {  
        super(context);  
    }  
  
    public ResizableImageView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){  
        Drawable d = getDrawable(); 
        if(d!=null){  
            int width = MeasureSpec.getSize(widthMeasureSpec);  
            //高度根據(jù)使得圖片的寬度充滿屏幕計(jì)算而得  
            int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());  
            setMeasuredDimension(width, height);  
        }else{  
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        }  
    }  
}  

在布局文件 xml 中,ImageView 不用對顯示方式進(jìn)行設(shè)置(使用默認(rèn)就行)。

2、設(shè)置 ImageView 的屬性

<ImageView
        android:id="@+id/iv_ocnyang"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:scaleType="fitXY"
        />

fitXY 這種圖片的顯示方式的效果是:根據(jù) ImageView 設(shè)置的大小拉伸圖片以填充滿空間,(單獨(dú)設(shè)置此屬性時(shí))圖片會(huì)變形。
adjustViewBounds 是限制圖片在顯示時(shí)保持原圖比例。(和 fitXY 顯示方式合用能到達(dá)自適應(yīng)的效果)

通過這上面兩種方式顯示圖片一般都能夠?qū)挾瘸錆M高度自適應(yīng)的效果,可是當(dāng)你用 Glide 請求顯示網(wǎng)絡(luò)圖片的時(shí)候,你會(huì)很失望的發(fā)現(xiàn)上面的設(shè)置失效了同時(shí)圖片也變形了。

那么這時(shí)候是哪里出了問題了呢?(下面只做一個(gè)籠統(tǒng)的分析,具體可以看這個(gè)鏈接: Glide使用及注意的地方
其實(shí)如果你熟知 Glide 的話,可能你還記得,Glide 在加載圖片的時(shí)候,加載的大小會(huì)和 ImageView 的大小保持一致。也就是 ImageView 的大小決定了 Glide 加載圖片的尺寸。而這里我們的 ImageView 設(shè)置的高度是 wrap_content,Glide 就無法準(zhǔn)確的加載圖片的大小了。

那這個(gè)時(shí)候怎么才能保證按原圖的比例來自適應(yīng)高度顯示呢?

這里有兩種方式:

  1. 你已經(jīng)知道圖片(或其他方式提前知道)圖片的比例,然后在用 Glide 請求圖片時(shí)限制圖片的加載大小,即設(shè)置 override(int width, int height) 。這時(shí)候加載到的圖片是原圖比例,顯示的時(shí)候雖然有拉伸/壓縮但都會(huì)保存原比例的。這種方式適用于你加載的圖片大小都比較規(guī)范固定的時(shí)候。

  2. 當(dāng)然,你請求的圖片源并不一定大小都一致。那這時(shí)候就可以使用下面這種方式了。這種方式的原理是,先使用 Glide 把圖片的原圖請求加載過來,然后再按原圖來顯示圖片。

    Glide.with(mContext)
    .load(url)
    .asBitmap()
    .into(new SimpleTarget<Bitmap>() {
    @Override
    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
    ivOcnyang.setImageBitmap(resource);
    }
    });

這兩種方法中,其實(shí)更加提倡的是第一種方式,因?yàn)檫@種方式不會(huì)造成任何負(fù)面的影響。但第二種方式,由于Glide加載圖片時(shí)是以全分辨率加載的,當(dāng)加載圖片過大且圖片很多時(shí),可能造成 OOM。同時(shí)第二種方式使用在列表上復(fù)用時(shí)會(huì)造成條目錯(cuò)亂錯(cuò)位。

2、Glide 加載 Gif 圖片的那些坑

我們知道,對比其他幾大圖片加載框架,我們更青睞 Glide 有一部分原因就是因?yàn)樗茱@示動(dòng)態(tài)圖,畢竟像下面這種圖是讓我最高興最無法拒絕的。

每看到星期五,這兩個(gè)字我就莫名的興奮

2.1、加載 Gif 圖片慢或者顯示不出來

這是一個(gè)公認(rèn)的問題了,在 Glide 的 issue 上有人提出過,并且作者也給出了解決方案。
加載 GIF 時(shí)需要調(diào)用 asGif() 方法,同時(shí)設(shè)置特別的緩存策略,調(diào)用 diskCacheStrategy() 將緩存策略設(shè)置為 SOURCE(緩存原圖) 或者 NONE(不做緩存)。

Glide 在加載 GIF 時(shí)不調(diào)用 asGif() 方法也是能正常顯示動(dòng)畫的。但建議調(diào)用 asGif()。

if (imgUrl.toUpperCase().endsWith(".GIF")) {
            Glide.with(mContext)
                    .load(imgUrl)
                    .asGif()
                    .override(width, height)
                    .placeholder(placeholderImg)
                    .error(errorImg)
                    .dontAnimate() //去掉顯示動(dòng)畫
                    .centerCrop()
                    .diskCacheStrategy(DiskCacheStrategy.SOURCE) //DiskCacheStrategy.NONE
                    .into(ivOcnyang);
        } else {
            Glide.with(mContext)
                    .load(imgUrl)
                    .override(width, height)
                    .placeholder(placeholderImg)
                    .error(errorImg)
                    .crossFade()
                    .centerCrop()
                    .into(ivOcnyang);
        }

2.2、動(dòng)態(tài)GIF圖片顯示的次數(shù)

可能你有時(shí)有需求,需要設(shè)置動(dòng)態(tài)圖的顯示一定次數(shù)時(shí)停止。

Glide.with(mContext)
        .load(imgUrl)
        .asGif()
        .override(width,height)
        .placeholder(placeholderImg)
        .error(errorImg)
        .dontAnimate()
        .centerCrop()
        .diskCacheStrategy(DiskCacheStrategy.SOURCE)
        .into(new GlideDrawableImageViewTarget(ivOcnyang, 3));

這里的 GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) 的第二個(gè)參數(shù) maxLoopCount 就是你要循環(huán)的次數(shù)。

2.3、將 GIF 作為 Bitmap 顯示

如果要顯示的圖片列表包含多種圖像類型, 有圖片和 GIF, 全都強(qiáng)制判斷 GIF 有時(shí)是不可行的. 我們可以將 GIF 先作為 Bitmap 加載第一幀圖像. 然后給用戶一個(gè)提示, 當(dāng)用戶點(diǎn)擊時(shí), 再使用 GIF 方式重新加載。

Glide  
    .with(context)
    .load(gifUrl)
    .asBitmap()
    .into(imageViewGifAsBitmap);

3、Glide圖片和默認(rèn)圖交替過程中,默認(rèn)圖閃爍一下

這是比較坑的一點(diǎn),如果占位圖要比原圖大有時(shí)會(huì)出現(xiàn)這種問題。其實(shí)有時(shí)候你會(huì)發(fā)現(xiàn)占位圖和 loading 圖的設(shè)置有時(shí)會(huì)造成各種問題,有時(shí)可能影響圖片顯示不正常。

解決方法:
去掉動(dòng)畫:dontAnimate()

有時(shí),使用 CircleImageView 加載圖片時(shí)顯示不正常,有可能是CircleImageView引起的與占位圖和顯示動(dòng)畫的沖突,解決方法同上,亦或去掉占位圖。

4、CenterCrop與Transformer的共存問題

這個(gè)問題的 issues 地址

這個(gè)問題是在網(wǎng)格布局和瀑布流布局中使用 .centerCrop,所以必須要在 ImageView 中去設(shè)置 scaleType 為 centerCrop。
但是,當(dāng)你同時(shí)給圖片設(shè)置圓角類 Transformer 時(shí),即在 Glide 加載圖片時(shí)給 .transform() 配置了一個(gè)圓角矩形,如果同時(shí) ImageView 的 scaleType 設(shè)置了 centerCrop,那圓角就沒有了。

解決方法,設(shè)置兩個(gè) Transformer:

...
.transform(new CenterCrop(getContext())
          ,new GlideRoundTransform(getContext(), 25))
...

這里給出圓角 GlideRoundTransform 源代碼。(設(shè)置圓形圖片,更多方式可以參考這個(gè) How to round an image with Glide library?

public class GlideRoundTransform extends BitmapTransformation {

    private static float radius = 0f;

    public GlideRoundTransform(Context context) {
        this(context, 4);
    }

    public GlideRoundTransform(Context context, int dp) {
        super(context);
        this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
    }

    @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return roundCrop(pool, toTransform);
    }

    private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;

        Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
        canvas.drawRoundRect(rectF, radius, radius, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName() + Math.round(radius);
    }
}

5、得到類似 You cannot start a load for a destroyed activity 這樣的異常

解決這個(gè)辦法只需在使用 Glide 時(shí)記?。翰灰俜侵骶€程里面使用 Glide 加載圖片,如果真的使用了,請把 context 參數(shù)換成 getApplicationContext。

6、一些使用技巧

1. 列表預(yù)加載
如果你想讓列表預(yù)加載的話,不妨試一下ListPreloader這個(gè)類。

2. 列表滑動(dòng)時(shí)取消請求
當(dāng)列表在滑動(dòng)的時(shí)候,調(diào)用 pauseRequests() 取消請求,滑動(dòng)停止時(shí),調(diào)用 resumeRequests() 恢復(fù)請求。

Glide.with(context).resumeRequests()
Glide.with(context).pauseRequests()

3. 清除所有加載請求
當(dāng)你想清除掉所有的圖片加載請求時(shí),可以使用 Glide.clear() 這個(gè)方法。

4. Glide特效轉(zhuǎn)換庫
glide-transformations 一個(gè)基于Glide的transformation庫,擁有裁剪,著色,模糊,濾鏡等多種轉(zhuǎn)換效果。

5. Palette 庫
GlidePalette 一個(gè)在Glide加載時(shí)很方便使用Palette的庫。

參考來源:
http://blog.csdn.net/easion_zms/article/details/50263409
http://www.itdecent.cn/p/4a3177b57949
http://answerzhao.github.io/2016/10/16/issues%20in%20using%20Glide/
http://blog.csdn.net/s569646547/article/details/54090034

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容