从设计到实现,一步步教你实现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 = 9;
    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 % 180 == 0) {
            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[1];
        GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
        int maxBitmapDimension = Math.max(maxTextureSize[0], 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 <= 0) width = maxImageSize.getWidth();

        int height = imageAware.getHeight();
        if (height <= 0) 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 / 2;
            final int halfHeight = srcHeight / 2;
            while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||
                scale *= 2;
            }
        } else {
            scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max
        }
        break;
    case CROP:
        if (powerOf2Scale) {
            final int halfWidth = srcWidth / 2;
            final int halfHeight = srcHeight / 2;
            while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // &&
                scale *= 2;
            }
        } 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 *= 2;
            } else {
                scale++;
            }
        }
        return scale;
    }

原来我们是用初始尺寸与计算后的 scale 值相除,判断缩放后的长/宽是否超过我们定义的最大图片尺寸,如果超过了,则需要增大 scale 值以增大缩放的比例。

computeMinImageSampleSize() 方法挺简单的就不分析拉
computeImageScale() 方法则与 computeImageSampleSize() 方法相反,求由目标尺寸求初始尺寸的缩放比例

IoUtils、L

其实 IoUtils 和 L 类中的方法都满简单的,就是对一些常用的操作进行了封装,简化我们的实际代码量。

你可能感兴趣的:(android)