天天看點

從設計到實作,一步步教你實作Android-Universal-ImageLoader-工具類

轉載請标明出處,本文出自:chaossss的部落格

在上一篇博文中我們分析了 AUImgLoader 緩存子產品的功能實作和架構設計,今天不妨着手分析 AUImgLoader 的工具類,為後面的分析作鋪墊。

在 Utils 包中,有 AUImgLoader 可能用到的工具類。其中 DiskCacheUtils、StorageUtils、MemonryCacheUtils 我們在分析 AUImgLoader 緩存功能子產品時已經講解過了,今天就不再贅述。事實上,AUImgLoader 中作為工具被實作的類不僅僅隻有 Utils 包中的類,例如還有 FileNameGenerator 以及下載下傳等等……廢話不多說,進入正題吧:

ImageSizeUtils

Provides calculations with image sizes, scales

ImageSizeUtils 類隻有200多行的代碼,主要功能是圖檔長寬和圖檔比例計算,在類中主要處理 ImageSize 對象,那麼 ImageSize 對象是什麼呢?

ImageSize

public class ImageSize {

    private static final int TO_STRING_MAX_LENGHT = ;
    private static final String SEPARATOR = "x";

    private final int width;
    private final int height;

    public ImageSize(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public ImageSize(int width, int height, int rotation) {
        if (rotation %  == ) {
            this.width = width;
            this.height = height;
        } else {
            this.width = height;
            this.height = width;
        }
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public ImageSize scaleDown(int sampleSize) {
        return new ImageSize(width / sampleSize, height / sampleSize);
    }

    public ImageSize scale(float scale) {
        return new ImageSize((int) (width * scale), (int) (height * scale));
    }

    @Override
    public String toString() {
        return new StringBuilder(TO_STRING_MAX_LENGHT).append(width).append(SEPARATOR).append(height).toString();
    }
}
           

SEPARATOR

是一個分隔符,用于分開圖檔的長與寬

TO_STRING_MAX_LENGHT

表示表示圖檔尺寸的字元串的最大長度,例如:9999x9999

ImageSize 的構造方法要求我們傳入長(height)和寬(width),scaleDown() 和 scale() 方法則是對圖檔的長寬進行處理,縮小/放大圖檔尺寸。總的來說,ImageSize 就是一個簡單的表示圖檔尺寸的類吧。

ImageSizeUtils 中的靜态方法

現在我們已經知道 ImageSize 是啥拉,那就繼續對 ImageSizeUtils 的分析吧:

首先獲得 ImageSizeUtils 單例一定會執行下面這段代碼:

static {
        int[] maxTextureSize = new int[];
        GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, );
        int maxBitmapDimension = Math.max(maxTextureSize[], DEFAULT_MAX_BITMAP_DIMENSION);
        maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension);
    }
           

這段代碼主要作用是確定圖檔尺寸沒有超過我們定義的最大尺寸,也就是2048x2048。

defineTargetSizeForView()

在 defineTargetSizeForView() 方法中,如果 ImageAware 的 wid 和 height 大于0,我們将傳回以 ImageAware 的長寬為圖檔尺寸的 ImageSize 對象;如果小于0,則傳回 maxImageSize 對象中存儲的長寬值構造的 ImageSize 對象。

public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) {
        int width = imageAware.getWidth();
        if (width <= ) width = maxImageSize.getWidth();

        int height = imageAware.getHeight();
        if (height <= ) height = maxImageSize.getHeight();

        return new ImageSize(width, height);
    }
           

ImageAware

那 ImageAware 對象又是什麼呢?

public interface ImageAware {

    int getWidth();

    int getHeight();

    ViewScaleType getScaleType();

    View getWrappedView();

    boolean isCollected();

    int getId();

    boolean setImageDrawable(Drawable drawable);

    boolean setImageBitmap(Bitmap bitmap);
}
           

我們可以看到,ImageAware 是一個接口,從作者對它的定義我們可以知道,在使用 ImageLoader 的過程中,ImageAware 提供圖檔處理/顯示所需要的一切屬性。此外,通過它的回調方法我們可以獲得所有通過 ImageLoader 擷取圖檔進而顯示圖檔的 View。

computeImageSampleSize() 方法

我們繼續往下分析,computeImageSampleSize() 方法看起來有些長,我們一點一點地看:

computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
            boolean powerOf2Scale)
           

首先,這個方法的四個參數分别代表:

  • srcSize-初始(圖檔)尺寸
  • targetSize-目标(顯示圖檔的 View)尺寸
  • viewScaleType-圖檔在 View 中的顯示方式(縮放/部分顯示等等……)
  • powerOf2Scale-圖檔尺寸是否為2的倍數

在方法内部,我們首先獲得初始尺寸和目标尺寸的長寬,然後初始化表示圖檔比例的變量 scale 為1,進入一個 switch 選擇語句對 scale 變量進行處理:

switch (viewScaleType) {
    case FIT_INSIDE:
        ……
        break;
    case CROP:
        ……
        break;
}
           

在選擇語句中,出現了 FIT_INSIDE 和 CROP 兩個值,诶,這兩個值我們沒有見過,到底是啥……ctrl+滑鼠點進去我們發現原來它兩都是 ViewScaleType 枚舉值:

public enum ViewScaleType {
    FIT_INSIDE,

    CROP;
}
           

從注釋來看,使用 FIT_INSIDE 時,圖檔的長和寬至少有一個值會小于或等于 View 的長或寬,以保證圖檔能在 View 中完整地被顯示(但有可能被縮放);而使用 CROP 時,圖檔的長和寬都會大于或等于 View 的長和寬,使得圖檔可能會顯示不完整,因為會被裁減。

搞清楚這兩個值的使用,我們就可以繼續向下分析拉:

switch (viewScaleType) {
    case FIT_INSIDE:
        if (powerOf2Scale) {
            final int halfWidth = srcWidth / ;
            final int halfHeight = srcHeight / ;
            while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||
                scale *= ;
            }
        } else {
            scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max
        }
        break;
    case CROP:
        if (powerOf2Scale) {
            final int halfWidth = srcWidth / ;
            final int halfHeight = srcHeight / ;
            while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // &&
                scale *= ;
            }
        } else {
            scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min
        }
        break;
}
           

因為兩個判斷塊中的處理很相似,我就隻抽 FIT_INSIDE 中的情況來分析吧:

首先檢查 powerOf2Scale 的值,如果 powerOf2Scale 為 false,表示圖檔尺寸不是2的倍數,那麼直接用初始值除以目标值,取比例最小的結果為 scale 值即可。

如果 powerOf2Scale 為 true,表示圖檔尺寸為2的倍數,則要先獲得初始尺寸的1/2,然後通過循環不斷讓 scale 的值乘2,進而不斷二分 halfwidth 和 halfHeight,直到兩者中有一個大于目标尺寸,循環結束,此時 scale 的值則是最終值。

由于圖檔的顯示必須占滿整個 View,是以通過選擇語句處理 scale 後,需要檢查 scale 的值,如果小于1,則要将 scale 的值置為1。然後執行 considerMaxTextureSize() 方法。

considerMaxTextureSize() 方法

我們在執行完成 computeImageSampleSize() 方法後得到了處理後的 scale 值,在方法的最後我們還需要通過 considerMaxTextureSize() 方法處理 scale 值,那麼 considerMaxTextureSize() 方法到底幹了什麼呢?

private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {
        final int maxWidth = maxBitmapSize.getWidth();
        final int maxHeight = maxBitmapSize.getHeight();
        while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {
            if (powerOf2) {
                scale *= ;
            } else {
                scale++;
            }
        }
        return scale;
    }
           

原來我們是用初始尺寸與計算後的 scale 值相除,判斷縮放後的長/寬是否超過我們定義的最大圖檔尺寸,如果超過了,則需要增大 scale 值以增大縮放的比例。

computeMinImageSampleSize() 方法挺簡單的就不分析拉

computeImageScale() 方法則與 computeImageSampleSize() 方法相反,求由目标尺寸求初始尺寸的縮放比例

IoUtils、L

其實 IoUtils 和 L 類中的方法都滿簡單的,就是對一些常用的操作進行了封裝,簡化我們的實際代碼量。