欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

分享一个轻量级图片加载类 ImageLoader

程序员文章站 2024-03-06 10:57:37
imageloader 这类的 图片加载网络上一大推,像比较出名的有nostra13 的-image-loader图片加载,xutil的图片加载,还有 facebook 的...

imageloader 这类的 图片加载网络上一大推,像比较出名的有nostra13 的-image-loader图片加载,xutil的图片加载,还有 facebook 的 fresco 。很多,但本着求学的态度,最近在做项目时有图片加载这个需求就自己写了个轻量级的 (本地)图片缓存加载 功能,分享给各位。

里面涉及了 lrucache ,executorservice,处理大图的 bitmapfactory 原理,view.settag() .

好了,不多说,先一步一步来:
首先看一下我封装的类怎么使用:

    // 本地照片绝对路径
    string imageurl = (string) t;
    // 得到 imageview 
    imageview grid_item = holder.getview(r.id.grid_item);
    // 设置 tag ,标记用的,反正图片显示错位
    grid_item.settag(imageurl);
    
     /**
     * 显示 图片
     * 
     * @param context
     *      : 上下文
     * @param imageview
     *      : imageview 控件
     * @param sourcepath
     *      : 图片 地址
     * @param r_id
     *      : 默认 图片 id ,r.drowable.id;
     * @param callback
     *      :图片显示 回调
     */
 
    new imageloader().displaybmp(mcontext,grid_item, imageurl, r.drawable.img_bg,this);
 

是不是很简单。

接下来具体分析 imageloader 这个类:

都知道手机的内存有限,不可能将所有的图片都加进内存,所以android 提供了一个 lrucache 方法,用到的 算法 是 :近期最少使用算法 ,及在图片不断的加进缓存,最少使用的图片也在不断的移除缓存 ,从而避免的内存不够的问题。

lrucache 的初始化代码如下:

  public imageloader() {

    // 取应用内存的 8/1 作为 图片缓存用
    int cachesize = maxmemory / 8;
    // 得到 lrucache
    mlrucache = new lrucache<string, bitmap>(cachesize) {
      @override
      protected int sizeof(string key, bitmap bitmap) {
        return bitmap.getbytecount();
      }
    };

  }

  /**
   * 将图片存储到lrucache
   */
  public void putbitmaptolrucache(string key, bitmap bitmap) {
    if (getbitmapfromlrucache(key) == null && mlrucache != null) {
      mlrucache.put(key, bitmap);
    }
  }
  /**
   * 从lrucache缓存获取图片
   */
  public bitmap getbitmapfromlrucache(string key) {
    return mlrucache.get(key);
  }

lrucache 就像 hashmap 一样 利用put 和 get 得到缓存的东西。

接着看 图片的 具体加载,先把代码贴出来:

  /**
   * 显示 图片
   * 
   * @param context
   *      : 上下文
   * @param imageview
   *      : imageview 控件
   * @param sourcepath
   *      : 图片 地址
   * @param r_id
   *      : 默认 图片 id ,r.drowable.id;
   * @param callback
   *      :图片显示 回调
   */
  public void displaybmp(final context context, final imageview imageview, final string sourcepath, final int r_id,
      final imagecallback callback) {

    final string path;

    if (!textutils.isempty(sourcepath)) {
      path = sourcepath;

    } else {
      return;
    }
    // 先 试着 从 缓存 得到 图片 , path 作为 图片的 key
    bitmap bmp = mlrucache.get(path);

    if (bmp != null) {
      if (callback != null) {
        // 回调 图片 显示
        callback.imageload(imageview, bmp, sourcepath);
      }
      // imageview.setimagebitmap(bmp);
      return;
    }
    // 如果 bmp == null ,给 imageview 显示默认图片
    imageview.setimageresource(r_id);
    // 启动 线程池
    threadpoolutils.getexecutorservice().execute(new runnable() {
      bitmap bitmap = null;

      @override
      public void run() {
        // todo auto-generated method stub

        try {
          // 加载 图片 地址 对应 的 缩略图
          bitmap = revitionimagesize(imageview, sourcepath);
        } catch (exception e) {

        }
        if (bitmap == null) {
          try {
            // 如果 缩略图 没加载成功 显示 默认 设置的图片
            bitmap = bitmapfactory.decoderesource(context.getresources(), r_id);
          } catch (exception e) {
          }
        }
        if (path != null && bitmap != null) {
          // 将 缩略图 放进 缓存 , path 作为 key
          putbitmaptolrucache(path, bitmap);
        }

        if (callback != null) {
          handler.post(new runnable() {
            @override
            public void run() {
              // 回调 图片 显示
              callback.imageload(imageview, bitmap, sourcepath);

            }
          });
        }

      }
    });

  }

代码不是狠多,主要就是 先从缓存加载图片,当加载图片为空时,再从手机的图片地址加载图片

bitmap = revitionimagesize(imageview, sourcepath);

加载缓存图片就不多说了,看的也明白, mlrucache.get(key);就这么简单

具体分析 revitionimagesize() 这个方法吧:

  public bitmap revitionimagesize(imageview imageview, string path) throws ioexception {

    // 得到 布局 imageview 的 宽高
    int img_width = imageview.getwidth();
    int img_height = imageview.getheight();
 
    bufferedinputstream in = new bufferedinputstream(new fileinputstream(new file(path)));
    bitmapfactory.options options = new bitmapfactory.options();
    options.injustdecodebounds = true;

    bitmapfactory.decodestream(in, null, options);
    in.close();

    int height = options.outheight;
    int width = options.outwidth;

    bitmap bitmap = null;
 
    int insamplesize = 1;

    // 计算出实际宽高和目标宽高的比率
    final int heightratio = math.round((float) height / (float) img_height);
    final int widthratio = math.round((float) width / (float) img_width);
    // 选择宽和高中最小的比率作为insamplesize的值,这样可以保证最终图片的宽和高
    // 一定都会大于等于目标的宽和高。
    insamplesize = heightratio < widthratio ? heightratio : widthratio;
    // 调用上面定义的方法计算insamplesize值
    options.insamplesize = insamplesize;

    options.injustdecodebounds = false;
    in = new bufferedinputstream(new fileinputstream(new file(path)));
    bitmap = bitmapfactory.decodestream(in, null, options);

    in.close();
    return bitmap;
  }

代码我也写了注释了 ,一般在加载图片时都有对图片一定的压缩处理避免oom,所以上面的处理方法也是挺常见的,对要显示 图片 根据 imageview 控件大小进行一定的压缩。

如果对 图片压缩处理不是很理解的朋友这么我简单解释一下:

首先加载完图片:

bufferedinputstream in = new bufferedinputstream(new fileinputstream(new file(path)));

然后:

options.injustdecodebounds = true;

再接着:

int height = options.outheight;
int width = options.outwidth;

这时候注意 程序并没有把图片真正的加载进来,options.injustdecodebounds = true;

这句在起作用,但图片的宽和高 的信息我们却得到了,就可以处理压缩图片了!

压缩完图片再:

options.injustdecodebounds = false;

重新得到压缩后的图片:

bitmap = bitmapfactory.decodestream(in, null, options);

解释完毕。

仔细看代码的同学会发现 displaybmp() 方法里面有个 回调参数:

回调接口如下:

  /**
   * 显示图片回调
   * 
   * @author administrator
   *
   */
  public interface imagecallback {
    public void imageload(imageview imageview, bitmap bitmap, object... params);
  }

具体实现是在显示图片的地方回调的:

   /**
   * 图片 缓存回调
   */
  @override
  public void imageload(imageview imageview, bitmap bitmap, object... params) {
    if (imageview != null && bitmap != null) {
      string url = (string) params[0];

      // 判断 这里的 url 是否 对应 imageview.gettag()
      // 如果 将这句 判断 去掉 那么 就会出现 经常出现的 图片 显示 错位 问题 !!!!
      if (url != null && url.equals((string) imageview.gettag())) {

        ((imageview) imageview).setimagebitmap(bitmap);
      }
    }
  }

代码注释的地方也写了,不太理解的同学可以私信交流,另外附上我 github github连接上的源码,可以下载下了运行方便好理解:

为了你方便使用,在你的项目中添加如下依赖即可:

dependencies {
      compile 'com.zts:imageloader:1.1.1'

  }