universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法

在listview/gridview中使用UIL来display每个item的图片,当图片数量较多需要滑动滚动时会出现卡顿,而且加载过的图片再次上翻后依然会重复加载(显示设置好的加载中图片)

最近在使用UIL遇到了这个问题,相信这个问题许多使用UIL的人都碰到过

现在把解决方法贴出来给有同样问题的朋友做参考

先看下UIL的工作流程

在已经允许内存,存储卡缓存的前提下,当一个图片被请求display时,首先要判断图片是否缓存在内存中,如果false则尝试从存储卡读取,如果依然不存在最后才从网络地址下载

从内存读取的速度最快,存储卡次之,在我们滚动listview的时候,如果是从内存加载图片则会显得非常流畅,如果是存储卡就会先出现载入中图片然后再显示实际图片

我们通常认为已经读过一次的图片自然将会加入内存缓存中,那么下一次读取将是直接从内存中读取,但是实际上载入过的图片在滑动出屏幕再滑动回来后依然会再次从存储卡读取,这主要是UIL的缓存策略引起的一个"疑似BUG"

查看UIL的源码,displayImage函数

    public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
        displayImage(uri, new ImageViewAware(imageView), options, null, null);
    }

会有ImageAware接口的一个实例化,这个默认的实例化有个重要的参数 :checkActualViewSize 具体说明如下

public ViewAware(View view) {
        this(view, true);
    }

    /**
     * Constructor
     *
     * @param view                {@link android.view.View View} to work with
     * @param checkActualViewSize <b>true</b> - then {@link #getWidth()} and {@link #getHeight()} will check actual
     *                            size of View. It can cause known issues like
     *                            <a href="https://github.com/nostra13/Android-Universal-Image-Loader/issues/376">this</a>.
     *                            But it helps to save memory because memory cache keeps bitmaps of actual (less in
     *                            general) size.
     *                            <p/>
     *                            <b>false</b> - then {@link #getWidth()} and {@link #getHeight()} will <b>NOT</b>
     *                            consider actual size of View, just layout parameters. <br /> If you set ‘false‘
     *                            it‘s recommended ‘android:layout_width‘ and ‘android:layout_height‘ (or
     *                            ‘android:maxWidth‘ and ‘android:maxHeight‘) are set with concrete values. It helps to
     *                            save memory.
     */
    public ViewAware(View view, boolean checkActualViewSize) {
        if (view == null) throw new IllegalArgumentException("view must not be null");

        this.viewRef = new WeakReference<View>(view);
        this.checkActualViewSize = checkActualViewSize;
    }

这个参数会影响缓存时的key名称,当图片第一次缓存时,当时图片并未下载,自然无法获得图片的长宽尺寸,这时UIL会使用配置预设的maxwidth和maxheight为长宽,缓存的key名称为类似这样:url_widthxheight

在checkActualViewSize设置为true时,第二次载入图片的view将会读取view的长宽,这时的长宽会是图片的实际尺寸,相应的生成的缓存key名称也会变成url_realwidthxrealheight,这个名称同之前缓存的不同,因此也当然不能在缓存查询中命中

所以最后就需要再次从存储中加载图片,并以新的keyname再存一份副本到内存中

解决方法有两个:

1)设置imageview的layout_width和layout_height为实际图片长宽(假如你的图片都是固定尺寸的,这样做就OK了)

2)display的方法修改一下,不直接display imageview改为ImageAware,类似

ImageAware imageAware = new ImageViewAware(imageView, false);
imageLoader.displayImage(imageUri, imageAware);

显式的将checkActualViewSize设为false, 这样图片的缓存也将只会保存一个副本,保证第二次查询时可以直接命中

按上述方法设置之后一般来说在listview/gridview滑动时图片效果基本没什么大问题了,但是还有些额外设置也许也是大家需要注意的

首先是config的初始化

File cacheDir = StorageUtils.getOwnCacheDirectory(getApplicationContext(), "imageloader/Cache"); //缓存文件的存放地址
ImageLoaderConfiguration config = new ImageLoaderConfiguration
.Builder(getApplicationContext())
.memoryCacheExtraOptions(480, 800) // max width, max height
.threadPoolSize(3)//线程池内加载的数量
.threadPriority(Thread.NORM_PRIORITY - 2)  //降低线程的优先级保证主UI线程不受太大影响
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(5 * 1024 * 1024)) //建议内存设在5-10M,可以有比较好的表现
.memoryCacheSize(5 * 1024 * 1024)
.discCacheSize(50 * 1024 * 1024)
.discCacheFileNameGenerator(new Md5FileNameGenerator())
.tasksProcessingOrder(QueueProcessingType.LIFO)
.discCacheFileCount(100) //缓存的文件数量
.discCache(new UnlimitedDiscCache(cacheDir))
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.imageDownloader(new BaseImageDownloader(getApplicationContext(), 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)
.writeDebugLogs() // Remove for release app
.build();

然后是option的设置

options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.default_cover)
.showImageForEmptyUri(R.drawable.default_cover)
.showImageOnFail(R.drawable.default_cover)
.cacheInMemory(true)
.cacheOnDisc(true)
.imageScaleType(ImageScaleType.NONE)
.bitmapConfig(Bitmap.Config.RGB_565)//设置为RGB565比起默认的ARGB_8888要节省大量的内存
.delayBeforeLoading(100)//载入图片前稍做延时可以提高整体滑动的流畅度
.build();

滑动时禁止加载也可以有效的提高表现

setOnScrollListener(new PauseOnScrollListener(imageLoader, true, true));//两个分别表示拖动下拉条和滑动过程中暂停加载

最后就是在getview中,例行的viewholder保存状态之外,将URL存入imageview的tag中,通过对比URL值来减少UIL的display次数以提高表现

UIL是个非常不错的图片加载类的第三方库,可以帮我们在开发过程中省不少事,不过如果想用好也需要自己真正的去研究下

参考资料:

https://github.com/nostra13/Android-Universal-Image-Loader/issues/376

https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Task-flow

universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法

时间: 2024-10-05 04:20:56

universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法的相关文章

【Android】解决Android的ListView控件滚动时背景变黑

[转载]解决Android的ListView控件滚动时背景变黑 如果大家在非黑色背景下使用ListView控件时,Android默认可能在滚动ListView时这个列表控件的背景突然变成黑色.这样可能导致程序的黑色的背景和主程序的主题既不协调.解决的方法Google在设计Android时也考虑了,在Layout的ListView中加入android:cacheColorHint="#00000000" 的属性即可. 如: <?xml version="1.0"

ListView或者RecycleView滚动时隐藏Toolbar( Part 2 )

ListView或者RecycleView滚动时隐藏Toolbar( Part 2 ) > * 原文链接 : How to hide/show Toolbar when list is scrolling (part 2) * 译者 : chaossss * 校对者: 这里校对者的github用户名 * 状态 : 完成 Hello,各位小伙伴,俺胡汉三又来了!!!今天我打算接着上一篇博文继续给大家讲解展现/隐藏Toolbar的效果.我建议没有读过 ListView或者RecycleView滚动时

浅谈DevExpress&lt;三&gt;:在GridView中加载动态图片

今天的演示效果如下:在GridView中的下拉框中选中一种颜色,则后面的加载相应的图片,如下图: 1. 2. 3. 下面说下实现方法:首先在项目中拉一个GirdControl,在里面创建4列:ID,Name,Color,Image,并将Color和Image分别创建repositoryItemComboBox和repositoryItemPictureEdit控件,如下图: 将一个图片文件夹放到程序的启动目录中: 文件夹中包含如下图片: 接下来进行创建数据模板,先创建一个Datetable,添加

jQuery中ajax的使用与缓存问题的解决方法

jQuery中ajax的使用与缓存问题的解决方法 1:GET访问 浏览器 认为 是等幂的就是 一个相同的URL 只有一个结果[相同是指 整个URL字符串完全匹配]所以 第二次访问的时候 如果 URL字符串没变化 浏览器是 直接拿出了第一次访问的结果 POST则 认为是一个 变动性 访问 (浏览器 认为 POST的提交 必定是 有改变的) 防止 GET 的 等幂 访问 就在URL后面加上 ?+new Date();,[总之就是使每次访问的URL字符串不一样的] 设计WEB页面的时候 也应该遵守这个

VS2010中&lt;无法打开包括文件:“iostream.h”:&gt;错误解决方法

C/C++ code? 1 2 #include <iostream.h> 改为: C/C++ code? 1 2 #include <iostream> using namespace std; VS2010中<无法打开包括文件:"iostream.h":>错误解决方法

ajax回调函数中使用$(this)取不到对象的解决方法

如果在ajax的回调函数内使用$(this)的话,实践证明,是取不到任何对象的,需要的朋友可以参考下 $(".derek").each(function(){ $(this).click(function(){ var params = $(this).parent().serialize(); var obj=$(this).parent().siblings("div#caskContent"); var form=$(this).parent(); $.aja

Linux中npm出现npmlog找不到的解决方法

?Linux中npm出现npmlog找不到的解决方法 描述 今天在对nodejs项目进行服务器迁移的时,在新的linux服务器上输入 npm -v 命令后报 "cannot find module 'npmlog'-."的错误.找到的原因是拷贝过去的bin目录下的npm文件并没有链接到module目录下的npm-cli.js文件,所以导致在执行 npm -v 命令时,路径问题导致npmlog模块无法找到. 解决方法 解决的方法有两个,一个是删除bin目录下的npm文件,并用如下命令链接

spring加载过程中jar包加载不了,解决方法

当我们在开发spring项目时,一般会将jar包放到webInf/lib下,这样是myeclipse自动将jar包加载到tomcat中webapps下,但是当我们新建一个lib文件夹的情况下,我们add building Path时就会出错,这时候我们有个技巧供使用. 1.项目上点击右键搜索de,找到deployment assembly 目的就是将此处添加的jar包添加到系统webINF/lib路径下 来自为知笔记(Wiz) spring加载过程中jar包加载不了,解决方法

java中两double相加精度丢失问题及解决方法

在讨论两位double数0.1和0.2相加时,毫无疑问他们相加的结果是0.2.但是问题总是如此吗? 下面我们让下面两个doubles数相加,然后看看输出结果: @Test public void testBig(){ System.out.println(0.11+2001299.32); } 控制台输出2001299.4300000002 我们吃惊的发现,结果并不是我们预想的那样,这是为什么呢?又如何解决呢? 现贴出BigDecimal的一个构造函数的文档供大家参考 BigDecimal pu