ListView常见问题二
2017-03-24 17:20
99 查看
此篇接ListView常见问题一
Android-Universal-Image-Loader jar包下 listview item 图片重复问题
上片提到由于ListView ViewHolder的存在虽然大大优化了ListView但是在加载图片时会有重复的问题
先来简单介绍一下Android-Universal-Image-Loader jar包 Android-Universal-Image-Loader jar包有三个常用的组件DisplayImageOptions、ImageLoader、ImageLoaderConfiguration
ImageLoader讲解
ImageLoader是具体下载图片,缓存图片,显示图片的具体执行类,它有两个具体的方法displayImage(...)、loadImage(...),但是其实最终他们的实现都是displayImage(...)。
需要在全局application里配置
public static ImageLoader imageLoader = ImageLoader.getInstance();
ImageLoader主要有两个方法
displayImage(...)和loadImage(...)
在ImageLoader的源码中可以看出
loadImage(...)方法有四个重载方法
1.loadImage(String uri, ImageLoadingListener listener)
2.loadImage(String uri, ImageSize minImageSize, ImageLoadingListener listener)
3.loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener)
4.loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener)
其中前三个方法中只是调用了第四个方法而第四个方法有调用了displayImage(...)方法
所以loadImage(...)方法其实就是displayImage(...)方法
displayImage(...)方法方法也有四个重载方法
1.displayImage(String
uri, ImageView imageView)
2.displayImage(String uri, ImageView imageView, DisplayImageOptions options)
3.displayImage(String uri, ImageView imageView, ImageLoadingListener listener)
4.displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)
而和loadImage(...)方法一样displayImage(...)方法的前三个方法也是只是调用了第四个方法
所以观看源码时 只需查看ImageLoader的displayImage(String uri, ImageView imageView, DisplayImageOptions
options, ImageLoadingListener listener) 的源码即可
displayImage(...)四个参数方法的源码如下
源码分析
1.当传递的imageview为空时抛出“传递参数有误,文件找不到”的异常
if(listener ==null)
{
3.当传递的DisplayImageOptions 对象为空时 对象为自己类创建
获取ImageLoaderConfiguration
1.默认方式
ImageLoaderConfiguration configuration=ImageLoaderConfiguration.createDefault(context);
2.自定义方式
ImageLoaderConfiguration configuration=new ImageLoaderConfiguration.Builder(this)
.memoryCacheExtraOptions(480, 800)//内存缓存文件的最大长宽
.discCacheExtraOptions(480, 800, null)//本地缓存的详细信息
.threadPoolSize(3)//线程池内加载的数量
.threadPriority(Thread.NORM_PRIORITY-2)//设置当前线程的优先级
.tasksProcessingOrder(QueueProcessingType.FIFO)//设置任务的处理顺序
.denyCacheImageMultipleSizesInMemory()//防止内存中图片重复
.memoryCache(new LruMemoryCache(2*1024*1024))//设置自己的内存缓存大小 2M
.memoryCacheSize(2*1024*1024)
.memoryCacheSizePercentage(13)//内存缓存百分比
.discCache(new UnlimitedDiscCache(new File(sdPath+"/huang/image1")))//设置缓存的图片在sdcard中的位置
.discCacheSize(50*1024*1024)//硬盘缓存大小 50M
.discCacheFileCount(100)//硬盘缓存文件个数
.discCacheFileNameGenerator(new Md5FileNameGenerator())//md5加密的方式,或new HashCodeFileNameGenerator()
.imageDownloader(new BaseImageDownloader(this))
.imageDecoder(new BaseImageDecoder(true))//图片解码
.defaultDisplayImageOptions(getOption())//是否使用默认的图片加载配置,null表示不使用
.writeDebugLogs()
.build();
一般使用默认方式即可
ImageLoaderConfiguration configuration=ImageLoaderConfiguration.createDefault(context);
ImageLoaderConfiguration默认内存缓存使用的是LruMemoryCache缓存的是bitmap的强引用
构造方法 new LinkedHashMap(10, 0.75, true);
参数1:缓存的大小一般为10或是5。
参数2:加载因子 经验值为0.75。
参数3:true:按照最近访问量的高低排序,false:按照插入顺序排序。
最后初始化ImageLoaderConfiguration
Application.imageLoader.init(configuration);
DisplayImageOptions详解
options=new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.companylogo_default)
.showImageOnFail(R.drawable.companylogo_default)
.cacheInMemory(true)
.cacheOnDisc(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
displayImage源码图片路径为空时用到DisplayImageOptions设置的图片路径为空时的图片。
Android-Universal-Image-Loader jar包下 listview item 图片重复问题
上片提到由于ListView ViewHolder的存在虽然大大优化了ListView但是在加载图片时会有重复的问题
先来简单介绍一下Android-Universal-Image-Loader jar包 Android-Universal-Image-Loader jar包有三个常用的组件DisplayImageOptions、ImageLoader、ImageLoaderConfiguration
ImageLoader讲解
ImageLoader是具体下载图片,缓存图片,显示图片的具体执行类,它有两个具体的方法displayImage(...)、loadImage(...),但是其实最终他们的实现都是displayImage(...)。
需要在全局application里配置
public static ImageLoader imageLoader = ImageLoader.getInstance();
ImageLoader主要有两个方法
displayImage(...)和loadImage(...)
在ImageLoader的源码中可以看出
loadImage(...)方法有四个重载方法
1.loadImage(String uri, ImageLoadingListener listener)
2.loadImage(String uri, ImageSize minImageSize, ImageLoadingListener listener)
3.loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener)
4.loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options, ImageLoadingListener listener)
其中前三个方法中只是调用了第四个方法而第四个方法有调用了displayImage(...)方法
所以loadImage(...)方法其实就是displayImage(...)方法
displayImage(...)方法方法也有四个重载方法
1.displayImage(String
uri, ImageView imageView)
2.displayImage(String uri, ImageView imageView, DisplayImageOptions options)
3.displayImage(String uri, ImageView imageView, ImageLoadingListener listener)
4.displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)
而和loadImage(...)方法一样displayImage(...)方法的前三个方法也是只是调用了第四个方法
所以观看源码时 只需查看ImageLoader的displayImage(String uri, ImageView imageView, DisplayImageOptions
options, ImageLoadingListener listener) 的源码即可
displayImage(...)四个参数方法的源码如下
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) { this.checkConfiguration(); if(imageView == null) { throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)"); } else { if(listener == null) { listener = this.emptyListener; } if(options == null) { options = this.configuration.defaultDisplayImageOptions; } if(TextUtils.isEmpty(uri)) { this.engine.cancelDisplayTaskFor(imageView); listener.onLoadingStarted(uri, imageView); if(options.shouldShowImageForEmptyUri()) { imageView.setImageResource(options.getImageForEmptyUri()); } else { imageView.setImageDrawable((Drawable)null); } listener.onLoadingComplete(uri, imageView, (Bitmap)null); } else { ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache); String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize); this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey); listener.onLoadingStarted(uri, imageView); Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey); ImageLoadingInfo imageLoadingInfo; if(bmp != null && !bmp.isRecycled()) { if(this.configuration.loggingEnabled) { L.i("Load image from memory cache [%s]", new Object[]{memoryCacheKey}); } if(options.shouldPostProcess()) { imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri)); ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler()); this.engine.submit(displayTask1); } else { options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageView, bmp); } } else { if(options.shouldShowStubImage()) { imageView.setImageResource(options.getStubImage()); } else if(options.isResetViewBeforeLoading()) { imageView.setImageDrawable((Drawable)null); } imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri)); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler()); this.engine.submit(displayTask); } } } }
源码分析
1.当传递的imageview为空时抛出“传递参数有误,文件找不到”的异常
if(imageView == null) { throw new IllegalArgumentException("Wrong arguments were passed to displayImage() method (ImageView reference must not be null)"); }
2.当传递的ImageLoadingListener接口对象或是SimpleImageLoadingListener(
ImageLoadingListener接口的实现类 常用)为空时 监听为自己类创建的
private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener();
if(listener ==null)
{
listener = this.emptyListener; }
3.当传递的DisplayImageOptions 对象为空时 对象为自己类创建
if(options == null) { options = this.configuration.defaultDisplayImageOptions; }
4.当图片路径为空时
if(TextUtils.isEmpty(uri)) { this.engine.cancelDisplayTaskFor(imageView); listener.onLoadingStarted(uri, imageView); if(options.shouldShowImageForEmptyUri()) { imageView.setImageResource(options.getImageForEmptyUri()); } else { imageView.setImageDrawable((Drawable)null); } listener.onLoadingComplete(uri, imageView, (Bitmap)null); }
即此时
如果DisplayImageOptions对象设置了.showImageForEmptyUri(R.drawable.companylogo_default)
及路径为空时显示默认图片 此时就显示设置的图片
如果没设置图片显示为空
最后调用ImageLoadingListener接口对象或是SimpleImageLoadingListener(ImageLoadingListener接口的实现类 常用)onLoadingComplete方法
5.当路径不为空时
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache); String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize); this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey); listener.onLoadingStarted(uri, imageView); Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey); ImageLoadingInfo imageLoadingInfo; if(bmp != null && !bmp.isRecycled()) { if(this.configuration.loggingEnabled) { L.i("Load image from memory cache [%s]", new Object[]{memoryCacheKey}); } if(options.shouldPostProcess()) { imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri)); ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler()); this.engine.submit(displayTask1); } else { options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageView, bmp); } } else { if(options.shouldShowStubImage()) { imageView.setImageResource(options.getStubImage()); } else if(options.isResetViewBeforeLoading()) { imageView.setImageDrawable((Drawable)null); } imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri)); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler()); this.engine.submit(displayTask); }
即先从缓存中找
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, this.configuration.maxImageWidthForMemoryCache, this.configuration.maxImageHeightForMemoryCache); String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize); this.engine.prepareDisplayTaskFor(imageView, memoryCacheKey); listener.onLoadingStarted(uri, imageView); Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);如果缓存中的bitmap不为空且没有被回收
if(options.shouldPostProcess()) { imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri)); ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, options.getHandler()); this.engine.submit(displayTask1); } else { options.getDisplayer().display(bmp, imageView, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageView, bmp); }
DisplayImageOptions类的display()方法 并调用onLoadingComplete()方法
否则 bitmap为空或是已经被回收
if(options.shouldShowStubImage()) { imageView.setImageResource(options.getStubImage()); } else if(options.isResetViewBeforeLoading()) { imageView.setImageDrawable((Drawable)null); } imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, memoryCacheKey, options, listener, this.engine.getLockForUri(uri)); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, options.getHandler()); this.engine.submit(displayTask);
利用LoadAndDisplayImageTask 下载图片
final class LoadAndDisplayImageTask implements Runnable {
...
bmp = (Bitmap)this.configuration.memoryCache.get(this.memoryCacheKey); 下载成功后将图片放入缓存
}
ImageLoaderConfiguration详解ImageLoaderConfiguration的配置主要是全局性的配置,主要有线程类、缓存大小、磁盘大小、图片下载与解析、日志方面的配置。
获取ImageLoaderConfiguration
1.默认方式
ImageLoaderConfiguration configuration=ImageLoaderConfiguration.createDefault(context);
2.自定义方式
ImageLoaderConfiguration configuration=new ImageLoaderConfiguration.Builder(this)
.memoryCacheExtraOptions(480, 800)//内存缓存文件的最大长宽
.discCacheExtraOptions(480, 800, null)//本地缓存的详细信息
.threadPoolSize(3)//线程池内加载的数量
.threadPriority(Thread.NORM_PRIORITY-2)//设置当前线程的优先级
.tasksProcessingOrder(QueueProcessingType.FIFO)//设置任务的处理顺序
.denyCacheImageMultipleSizesInMemory()//防止内存中图片重复
.memoryCache(new LruMemoryCache(2*1024*1024))//设置自己的内存缓存大小 2M
.memoryCacheSize(2*1024*1024)
.memoryCacheSizePercentage(13)//内存缓存百分比
.discCache(new UnlimitedDiscCache(new File(sdPath+"/huang/image1")))//设置缓存的图片在sdcard中的位置
.discCacheSize(50*1024*1024)//硬盘缓存大小 50M
.discCacheFileCount(100)//硬盘缓存文件个数
.discCacheFileNameGenerator(new Md5FileNameGenerator())//md5加密的方式,或new HashCodeFileNameGenerator()
.imageDownloader(new BaseImageDownloader(this))
.imageDecoder(new BaseImageDecoder(true))//图片解码
.defaultDisplayImageOptions(getOption())//是否使用默认的图片加载配置,null表示不使用
.writeDebugLogs()
.build();
一般使用默认方式即可
ImageLoaderConfiguration configuration=ImageLoaderConfiguration.createDefault(context);
ImageLoaderConfiguration默认内存缓存使用的是LruMemoryCache缓存的是bitmap的强引用
LruMemoryCache源码
public class LruMemoryCache implements MemoryCacheAware<String, Bitmap> { private final LinkedHashMap<String, Bitmap> map; private final int maxSize; private int size; public LruMemoryCache(int maxSize) { if(maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } else { this.maxSize = maxSize; this.map = new LinkedHashMap(0, 0.75F, true); } } public final Bitmap get(String key) { if(key == null) { throw new NullPointerException("key == null"); } else { synchronized(this) { return (Bitmap)this.map.get(key); } } } public final boolean put(String key, Bitmap value) { if(key != null && value != null) { synchronized(this) { this.size += this.sizeOf(key, value); Bitmap previous = (Bitmap)this.map.put(key, value); if(previous != null) { this.size -= this.sizeOf(key, previous); } } this.trimToSize(this.maxSize); return true; } else { throw new NullPointerException("key == null || value == null"); } } private void trimToSize(int maxSize) { while(true) { synchronized(this) { if(this.size < 0 || this.map.isEmpty() && this.size != 0) { throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if(this.size > maxSize && !this.map.isEmpty()) { Entry toEvict = (Entry)this.map.entrySet().iterator().next(); if(toEvict != null) { String key = (String)toEvict.getKey(); Bitmap value = (Bitmap)toEvict.getValue(); this.map.remove(key); this.size -= this.sizeOf(key, value); continue; } } return; } } } public final void remove(String key) { if(key == null) { throw new NullPointerException("key == null"); } else { synchronized(this) { Bitmap previous = (Bitmap)this.map.remove(key); if(previous != null) { this.size -= this.sizeOf(key, previous); } } } } public Collection<String> keys() { synchronized(this) { return new HashSet(this.map.keySet()); } } public void clear() { this.trimToSize(-1); } private int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } public final synchronized String toString() { return String.format("LruCache[maxSize=%d]", new Object[]{Integer.valueOf(this.maxSize)}); } }
即使用了LinkedHashMapLinkedHashMap(LRU(Least Recently Used)策略,即当内存使用不足时,把最近最少使用的数据从缓存中移除,保留使用最频繁的数据。)
构造方法 new LinkedHashMap(10, 0.75, true);
参数1:缓存的大小一般为10或是5。
参数2:加载因子 经验值为0.75。
参数3:true:按照最近访问量的高低排序,false:按照插入顺序排序。
最后初始化ImageLoaderConfiguration
Application.imageLoader.init(configuration);
DisplayImageOptions详解
options=new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.companylogo_default)
.showImageOnFail(R.drawable.companylogo_default)
.cacheInMemory(true)
.cacheOnDisc(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
displayImage源码图片路径为空时用到DisplayImageOptions设置的图片路径为空时的图片。
相关文章推荐
- ListView+Adapter适配器的使用与Listview常见的UI显示问题
- listview常见问题整理
- listview之一些常见问题的解决方法
- ListView Adapter常见问题
- android listview选中某一行,成选中状态颜色高亮显示,ListView的UI显示中常见问题及解决办法
- React-native 常见问题:Warning 当使用组件 react-native-gifted-listview
- 安卓中常见ListView等加载Adapter后的错乱问题
- ListView常见问题三
- Android ListView 常见问题汇总 checkbox 点击 复用混乱
- ListView常见问题一
- Android 编程下 ListView 和 CheckBox 混合使用时的常见问题
- ListView 中使用onItemClick和onItemLongClick的常见问题
- Android ListView常见属性问题
- ListView深入源码解析及其常见问题解决方案
- android ListView组件使用常见问题或形式总结(一)
- android ListView常见问题解决方法(滚动背景变黑,去除滑动时阴影,拖动时Item图片不显示)
- Android控件ListView常见问题指南
- ListView+Adapter适配器的使用与Listview常见的UI显示问题
- 【安卓学习之常见问题】 Listview的setOnItemLongClickListener无法进入问题
- ListView的优化以及常见问题