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

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,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,options);

解释完毕。

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

回调接口如下:

  /**
   * 显示图片回调
   * 
   * @author Administrator
   *
   */
  public interface ImageCallback {
    public void imageLoad(ImageView imageView,Bitmap bitmap,Object... params);
  }

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

   /**
   * 图片 缓存回调
   */
  @Override
  public void imageLoad(ImageView imageView,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'

  }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


更新Android SDK到3.0版本时,遇到Failed to rename directory E:\android\tools to E:\android\temp\ToolPackage.old01问题,导致无法更新,出现该问题的原因是由于3.0版本与较早的sdk版本之间文件结构有冲突,解决
Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到back事件: 1)重写onKeyDown或者onKeyUp方法 2)重写onBackPressed方
Android实现自定义带文字和图片的Button 在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法。一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小。在Button的属性中有一个是drawable
Android中的&quot;Unable to start activity ComponentInfo&quot;的错误 最近在做一款音乐播放器的时候,然后在调试的过程中发现一直报这个错误&quot;Unable to start activity ComponentInfo&quot;,从字面
Android 关于长按back键退出应用程序的实现最近在做一个Android上的应用,碰到一个问题就是如何实现长按back键退出应用程序。在网上查找了很多资料,发现几乎没有这样的实现,大部分在处理时是双击back键来退出应用程序。参考了一下双击back键退出应用程序的代码,网上主流的一种方法是下面
android自带的时间选择器只能精确到分,但是对于某些应用要求选择的时间精确到秒级,此时只有自定义去实现这样的时间选择器了。下面介绍一个可以精确到秒级的时间选择器。 先上效果图: 下面是工程目录: 这个控件我也是用的别人的,好像是一个老外写的,com.wheel中的WheelView是滑动控件的主
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing
Android ListView的item背景色设置以及item点击无响应等相关问题 在Android开发中,listview控件是非常常用的控件,在大多数情况下,大家都会改掉listview的item默认的外观,下面讲解以下在使用listview时最常见的几个问题。1.如何改变item的背景色和按
如何向Android模拟器中导入含有中文名称的文件在进行Android开发的时候,如果需要向Android模拟器中导入文件进行测试,通过DDMS下手动导入或者在命令行下通过adb push命令是无法导入含有中文文件名的文件的。后来发现借用其他工具可以向模拟器中导入中文名称的文件,这个工具就是Ultr
Windows 下搭建Android开发环境一.下载并安装JDK版本要求JDK1.6+,下载JDK成功后进行安装,安装好后进行环境变量的配置【我的电脑】-——&gt;【属性】——&gt;【高级】 ——&gt;【环境变量】——&gt;【系统变量】中点击【新建】:变量名:CLASSPATH变量值:……
如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 在android中有时候可能要实现一个底部弹出菜单,此时可以考虑用PopupWindow来实现。下面就来介绍一下如何使用PopupWindow实现一个弹出窗。 主Activity代码:public void onCreat
解决Android中的ERROR: the user data image is used by another emulator. aborting的方法 今天调试代码的时候,突然出现这个错误,折腾了很久没有解决。最后在google上找到了大家给出的两种解决方案,下面给出这两种方法的链接博客:ht
AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具