1. 程式人生 > >使用Glide實現在非WiFi環境手動點選下載圖片(判斷Glide是否快取了圖片)

使用Glide實現在非WiFi環境手動點選下載圖片(判斷Glide是否快取了圖片)

1、概述

Glide作為Google推薦的一套快速高效的圖片載入框架,有很多人都在使用,我也不例外。不過在專案的需求中,難免會遇到一個這樣的需求:在非WiFi環境下,需要手動點選才能下載圖片
這初步實現起來是很簡單的,但一些細節卻不好解決。比如,在使用移動資料的情況下,我不能去自動載入圖片,但已經快取過的圖片我們得讓他自動顯示出來。這個時候我們會發現,Glide沒有直接的、明確的介面去立馬判斷某圖片(Url等)是否已經快取了。
為了實現這個功能,我們就只能自己去Glide的快取目錄尋找那圖片是否已經快取下來了,但Glide快取檔名字是不會是Url或圖片檔名,因此我們得采取一些其他手段。

若我的思路中有如何錯誤,歡迎指正。

2、實現

(PS. 以下程式碼是對在非WiFi環境下,需要手動點選才能下載圖片這一需求的完全實現)

(1)、初步的判斷

    /**
     * 載入圖片
     *
     * @param context      載入這個行為所處的Activity或Fragment
     * @param url          圖片的網址
     * @param imageView    載入到哪個ImageView上
     * @param widthPixels  圖片的寬
     * @param heightPixels 圖片的高
     */
public static void loadNetworkImage(final Context context, final String url, final ImageView imageView, final int widthPixels, final int heightPixels) { if (isLoadFailed(imageView)) { // 判斷是否是正在載入中的ImageView return; } imageView.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) { if (isLoadFailed(imageView)) { // 判斷是否載入失敗 loadImage(context, url, imageView, widthPixels, heightPixels); } else if (!isLoading(imageView) && sOnImageViewClickListener != null) { // 判斷是否載入中 與 是否有設定監聽 sOnImageViewClickListener.onClick(v); } } }); if (AppUtil.loadIsManualLoadPhotoInNotWiFi(context) && !NetworkUtil.isWifi()) { // 判斷使用者是否開啟了 在非WiFi環境下,需要手動點選才能下載圖片,後在判斷是否處於WiFi中 loadCacheImage(context, url, imageView, widthPixels, heightPixels); return; } loadImage(context, url, imageView, widthPixels, heightPixels); }

這裡的第一個if判斷是為了不讓ImageView在RecyclerView等控制元件中,應滑動關係而不斷重新整理佈局而設定的,避免多次對同一控制元件呼叫載入圖片。

中間的OnClickListener自然是為了讓圖片載入失敗後可以重新載入,或者是給使用者手動點選載入而用,其中裡面第二個if判斷是為了解決在載入成功後,還需要對圖片控制元件進行點選,從而進行其他邏輯(如QQ裡對載入完成的圖片進行檢視大圖)。

這裡需要注意,在每個Activity或Fragment被銷燬的時候,需要清空正在載入中的圖片集合與置空圖片點選監聽,避免出現問題。置空程式碼與判斷程式碼如下:

    /**
     * 正在載入中的控制元件集合
     */
    private static Set<ImageView> sImageViews = new HashSet<>();

    /**
     * 載入失敗所顯示的圖片
     */
    private static Drawable sErrorImg = ViewUtil.getDrawable(R.mipmap.img_default);

    /**
     * 判斷圖片是否載入失敗
     */
    private static boolean isLoadFailed(ImageView imageView) {
        return sErrorImg.equals(imageView.getDrawable());
    }

    /**
     * 判斷圖片是否在載入中
     */
    private static boolean isLoading(ImageView imageView) {
        return sImageViews.contains(imageView);
    }

    /**
     * 取消所有圖片載入,並置空監聽
     */
    public static void clearLoadingImg() {
        for (ImageView imageView : sImageViews) {
            Glide.clear(imageView);
        }
        sImageViews.clear();
        sOnImageViewClickListener = null;
    }

(2)、判斷圖片是否已經快取了

    private static void loadCacheImage(Context context, String url, ImageView imageView, int widthPixels, int heightPixels) {
        // 尋找快取圖片
        File file = DiskLruCacheWrapper.get(Glide.getPhotoCacheDir(context), 250 * 1024 * 1024).get(new OriginalKey(url, EmptySignature.obtain()));
        if (file != null) {
            loadImage(context, url, imageView, widthPixels, heightPixels);
        } else {
            imageView.setImageDrawable(sErrorImg);
            sImageViews.remove(imageView);
        }
    }

這裡就是這個需求中最大的難點了,如何去判斷Glide已經快取了該圖片。

通過閱讀、分析Glide的原始碼(也可以選擇去看郭神的Glide解析,網址也在上面發了),我們可以知道,Glide對其快取Key的構建是比較複雜的,有著十多個引數。但我們也會發現,Glide所快取的原圖的Key實際用到的引數只有兩個url和signature,而大多數情況下,signature只會是個空值。因此,我們只需要想辦法把url轉化成Glide的快取Key就大功告成了。
不過通過原始碼我們也知道,原圖的快取Key類——OriginalKey是預設的,也就是所,我們這些外部應用是無法使用。於是我就採用了一種取巧的方法,直接將Glide的這個類複製到自己的專案裡,該類程式碼如下:

    /**
     * Glide原圖快取Key
     */
    private static class OriginalKey implements Key {

        private final String id;
        private final Key signature;

        private OriginalKey(String id, Key signature) {
            this.id = id;
            this.signature = signature;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            OriginalKey that = (OriginalKey) o;

            return id.equals(that.id) && signature.equals(that.signature);
        }

        @Override
        public int hashCode() {
            int result = id.hashCode();
            result = 31 * result + signature.hashCode();
            return result;
        }

        @Override
        public void updateDiskCacheKey(MessageDigest messageDigest) throws UnsupportedEncodingException {
            messageDigest.update(id.getBytes(STRING_CHARSET_NAME));
            signature.updateDiskCacheKey(messageDigest);
        }
    }

這類裡有兩個引數,第一個就是我們圖片的Url,第二個則是簽名,用於方面標識圖片是否需要更新的,而這裡,我是直接選擇呼叫EmptySignature.obtain(),傳入一個空簽名。
在得到原圖的快取Key之後,我們就只需要得到Glide的磁碟快取了。

通過DiskLruCacheWrapper.get(File directory, int maxSize)方法,我們就可以得到Glide的磁碟快取物件DiskCache了(在這裡建議傳入Glide的快取目錄與250M。雖說Glide有進行判斷,如果快取物件以存在就直接把已存在的返回過來,但為了避免出現什麼莫名的異常,就按著Glide原始碼裡的呼叫方式使用)。
接著通過DiskCache.get(Key key)方法去獲得快取圖片檔案。

在找到快取檔案後為什麼不直接使用快取檔案載入呢,則是為了避免Glide的二次快取。我具體的Glide載入圖片方法都是用的同一個,裡面都是設定進行快取,如果這裡傳入的是快取檔案,就會導致二次快取,浪費!

(3)、具體的Glide載入圖片方法

    private static void loadImage(final Context context, Object url, final ImageView imageView, int widthPixels, int heightPixels) {
        DrawableRequestBuilder<Object> builder = Glide.with(context)
                .load(url)
                .placeholder(sLoadingImg)
                .error(sErrorImg)
                .diskCacheStrategy(DiskCacheStrategy.SOURCE) // 設定磁碟快取只快取原圖
                .skipMemoryCache(false) //進行記憶體快取
                .centerCrop();
        if (widthPixels != 0 || heightPixels != 0) {
            builder.override(widthPixels, heightPixels);
        }
        builder.into(new GlideDrawableImageViewTarget(imageView) {
            @Override
            public void onLoadStarted(Drawable placeholder) {
                super.onLoadStarted(placeholder);
                Animation rotationAnimation = AnimationUtils.loadAnimation(context, R.anim.rotate_loading);
                imageView.startAnimation(rotationAnimation);
                sImageViews.add(imageView); // 將其新增到 正在載入中的控制元件集合 中
            }

            @Override
            public void onLoadFailed(Exception e, Drawable errorDrawable) {
                super.onLoadFailed(e, errorDrawable);
                removeViewInLoadingSet();
            }

            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
                super.onResourceReady(resource, animation);
                removeViewInLoadingSet();
            }

            /**
             * 將 已載入完成/載入失敗的控制元件 從 正在載入中的控制元件集合 中移除
             */
            private void removeViewInLoadingSet() {
                imageView.clearAnimation();
                sImageViews.remove(imageView);
            }
        });
    }

最後這裡就是Glide載入的具體實現了,裡面我通過GlideDrawableImageViewTarget對載入狀態進行監聽,設定載入中的動畫。

到此,整個需求就完成了。

相關推薦

使用Glide實現在WiFi環境手動下載圖片判斷Glide是否快取圖片

1、概述 Glide作為Google推薦的一套快速高效的圖片載入框架,有很多人都在使用,我也不例外。不過在專案的需求中,難免會遇到一個這樣的需求:在非WiFi環境下,需要手動點選才能下載圖片。 這初步實現起來是很簡單的,但一些細節卻不好解決。比如,在使用移動

求解! vue2.0實現購物車小球掉落特效第一次動畫失效男默女淚的bug!

點選新增按鈕時有一個小球掉落到購物車的特效,第一次點選時動畫特效沒有出來,且沒有進入afterEnter方法。後面點選都沒有問題。 效果如圖:(第一次點選) 但是我在enter方法裡面下了一個斷點之後就有動畫效果,且進入了afterEnter。效果如下: 希望大

Android開發——後臺獲取使用者位置座標可獲取使用者支付寶密碼

1. getevent命令我們首先是根據adb shell getevent命令獲取到被點選位置的資訊。這裡要說明的是,不同的手機手機獲得的點選輸出是不一樣的。以我的真機為例,輸出如下/dev/inpu

Js實現抽獎轉盤,和返回某個模組頂部的功能

最近寫了幾個轉盤抽獎的活動頁面: 1.設定旋轉的角度: HTML部分:轉盤程式碼: <div class="lottery"> <div class="lottery_box"> <di

vue實現下載圖片

​<el-table-column label='縮圖' width='100'> <template slot-scope='scope'> <a :href="scope.row.url" download="">

ViewPager+RadioGroup+RadioButton實現滑動切換頁面與按鈕切換頁面

一:效果圖: 二:程式碼: 首先  根據我們有幾個頁面就設定幾個Fragment, 主函式: public class MainActivity extends AppCompatActivity { private ViewPager viewpager;

微信小程式實現給迴圈列表新增類單項和多項

在微信小程式裡面沒有DOM物件, 不能操作DOM. 所有的操作通過資料來實現,下面主要實現了給迴圈列表點選新增類的操作 一、單項 目標需求:實現下圖,給點選的view增加類,每次只能選擇一個。 主要思路:給點選的view增加類,

實現element-ui中table一行展開

轉:https://www.jianshu.com/p/e51ba4cb11d6 先上效果   效果圖 三要素 1、row-click 點選行 2、ref 自行了解vue 3

unity3d實現3D物體上的事件

首先要在攝像機中新增元件Physics Raycaster void Update () { if(Input.GetMouseButtonUp(0)){ Ray ray = Camera.main.ScreenPointToRay(Input.mousePosi

WIN10環境通知欄圖示時自動切換輸入法導致圖示位置變動

    這個問題由來已久,每當點選系統右下角工作列中的按鈕時,原本是搜狗輸入法就會自動變成“US [ 中文(簡體,中國) ]”,圖示會自動錯位,導致響應的是其他功能。     假設上圖是正常的環境,此時我點選電池圖示,工作列就會變成下圖樣式。

Springboot/SpringMVC+POI 實現Excel匯出功能(下載方式實現

@RequestMapping("/download") public void downstudents(HttpServletRequest request, HttpServletResponse response,@RequestParam String startTime, @Reques

JQ實現返回頂部有動畫過渡

$(function(){ //當滾動條的位置處於距頂部100畫素以下時,跳轉連結出現,否則消失 $(function () { $(window).scroll(function(){ if ($(window).sc

實現Material Design風格的水波盪漾效果

自Material Design的設計理念推出以來,整個安卓的UI跟UE界幾乎發生了翻天覆地的變化,由此推出的設計理念也在一步一步的被引入到開發者的具體專案中,良心來說Google提出的這個設計理念跟相關的支援包確實也給開發者帶來極大的便利,但是不足的是,向下相容做的不是太

解決RecyclerView實現聊天介面,但下面的EditText後彈出的輸入法會遮蓋RecyclerView內容的方法

學習Android也將近4個年頭了,一直想寫點自己所學的內容來幫助後來人,為網際網路奉獻自己的一份力量,也算自己的積累。但是之前由於自己的惰性一直沒有下筆,那就從今天這一篇開始吧! 我們做Android開發經常會有做即時聊天的需求,產品經理不管做啥APP,都要塞個IM,那

Android實現ListView顯示資訊,每個item,跳轉到相應介面

介面如下:(做這個目的僅僅是為了學習一點小知識,因為自己才剛開始) 實現的方法比較簡單,就是定義一個ListView,然後設定監聽,ListView對每個條目的監聽是setOnItemClickListener。 onItemClick(AdapterView&

recycleview實現title橫向滑動,條目居中顯示

首先這種需求常規有兩種做法, 第一種:動態建立TextView 然後放入到LinearLayout,Linearlayout在HorizontalScrollView中; 第二種:就是HorizontalScrollView + GridView; 這裡

ECharts 圖表區域的事件不觸發問題

1. 通過 myChart.getZr().on('click', fn) 監聽整個圖表的點選事件,註冊回撥 myChart.getZr().on('click', () => { //拿到index即可取出被點選資料的所有資訊 console.log(clickIndex) })

JS實現按下按鍵觸發事件

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS實現按下按鍵觸發點選事件</title> </head> &

Android富文字實現不同的文字新增事件

如果可以點選的文字位置是固定不變的,可以在String.xml中配置(如:“可以點選的”給“點選”設定點選事件)。 @Override protected void onCreate(Bundle s

css實現的輪播和切換(無js版)

https://github.com/lingxuanHuang/carousel-noJS .slide{ position: relative; margin:auto; width: 600px; height: 200px;