主要介绍一个支持图片自动预取、支持多种缓存算法、支持二级缓存、支持数据保存和恢复的图片缓存的使用、功能及网友反馈的常见问题解答。
更多 Android 图片缓存开源项目可见 Android 图片缓存。
与Android LruCache相比主要特性:(1). 使用简单 (2). 轻松获取及预取新图片 (3). 包含二级缓存 (4). 可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法 (5). 可方便的保存及初始化恢复数据 (6). 支持文件sd卡保存及自定义文件名规则 (7). 省流量性能佳(有且仅有一个线程获取图片) (8). 支持不同类型网络处理 (9). 可根据系统配置初始化缓存 (10). 扩展性强 (11). 支持等待队列 (12). 包含map的大多数接口。
适用:获取图片较多的应用,如新浪微博、twitter、微信头像、美丽说、蘑菇街、花瓣、淘宝等等。
本文以用ImageCache实现瀑布流为例,示例代码地址见ImageCacheDemo,效果图如下:
Demo APK 可以方便的查看效果,在各大应用商店搜索 trinea android 下载即可,如:Google Play。
1、使用
(1)引入公共库
引入TrineaAndroidCommon@Github(欢迎star和fork^_^)作为你项目的library(如何拉取代码及添加公共库),或是自己抽取其中的ImageCache@Github部分使用。
(2)调用
仅需简单三步:
a. AndroidManifest.xml中添加权限
1 2 |
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
b. 定义缓存和回调接口
基本使用:只对ImageView加载图片有效,定义只需要一条语句
1 |
public static final ImageCache IMAGE_CACHE = CacheManager.getImageCache(); |
高级使用:自定义图片获取成功的回调接口,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/** image cache **/ public static final ImageCache IMAGE_CACHE = new ImageCache(); static { OnImageCallbackListener imageCallBack = new OnImageCallbackListener() { private static final long serialVersionUID = 1L; // callback function before get image, run on ui thread @Override public void onPreGet(String imageUrl, View view) { // Log.e(TAG_CACHE, "pre get image"); } // callback function after get image successfully, run on ui thread @Override public void onGetSuccess(String imageUrl, Bitmap loadedImage, View view, boolean isInCache) { // can be another view child, like textView and so on if (view != null && loadedImage != null && view instanceof ImageView) { ImageView imageView = (ImageView)view; imageView.setImageBitmap(loadedImage); } } // callback function after get image failed, run on ui thread @Override public void onGetFailed(String imageUrl, Bitmap loadedImage, View view, FailedReason failedReason) { Log.e(TAG_CACHE, new StringBuilder(128).append("get image ").append(imageUrl).append(" error") .toString()); } @Override public void onGetNotInCache(String imageUrl, View view) { } }; IMAGE_CACHE.setOnImageCallbackListener(imageCallBack); } |
c. 需要加载图片的地方调用get(String imageUrl, View view)异步加载图片
1 |
IMAGE_CACHE.get(imageUrl, imageView); |
上面代码中new ImageCache()用来定义图片缓存,默认会根据系统内存设置缓存大小。默认缓存算法为LFU(Least Frequently Used),最近最少使用先删除。
setOnImageCallbackListener(imageCallBack);设置图片获取成功回调接口,其中onPreGet为图片获取前的回调函数,onGetSuccess为图片获取成功的回调函数,onGetFailed为图片获取失败的回调函数。
可以在程序退出(比如”退出确认对话框”点击确认)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如Application的onCreate函数)时调用initData(Context context, String tag)初始化恢复数据。其中tag为此ImageCache的标识。
get(imageUrl, imageView)异步获取图片,在图片获取成功后会自动调用onGetSuccess将参数传入。
get(String imageUrl,List<String> urlList, View view)异步获取图片,并且会自动预取urlList中的图片。
上面是最简单的实现。
第2部分会介绍支持的其他功能,包括网络类型设置、预取个数设置、多种缓存算法、保存及初始化恢复数据、等待队列开关、图片源读取方式设置、读取图片http设置、缓存有效时间、命中率及大部分map接口。
2、功能介绍
(1) 多种构造函数,可根据系统配置初始化缓存
public ImageCache()
public ImageCache(int primaryCacheMaxSize)
public ImageCache(int primaryCacheMaxSize, int secondaryCacheMaxSize)
public ImageCache(int primaryCacheMaxSize, int primaryCacheThreadPoolSize, int secondaryCacheMaxSize, int secondaryCacheThreadPoolSize)
支持四种构造函数,支持一级和二级缓存大小及获取图片线程池大小的设置。默认会根据系统可用内存大小设置缓存大小,根据系统Cpu个数设置线程池大小。
(2)、获取图片及自动预取
get(String imageUrl, View view)异步获取图片,在图片获取成功后自动调用OnImageCallbackListener的onGetSuccess函数,返回是否已在缓存中
get(String imageUrl, List<String> urlList, View view)异步获取图片,在图片获取成功后自动调用OnImageCallbackListener的onGetSuccess函数,并且根据imageUrl在urlList中的位置向前向后预取图片,返回是否已在缓存中。
public void setForwardCacheNumber(int forwardCacheNumber) 向前预取图片个数设置,默认为PreloadDataCache#DEFAULT_FORWARD_CACHE_NUMBER
public void setBackwardCacheNumber(int backwardCacheNumber)向后预取图片个数设置默认,默认为PreloadDataCache#DEFAULT_BACKWARD_CACHE_NUMBER
public CacheObject<V> get(K key)
public CacheObject<V> get(K key, List<K> keyList)
两个接口是直接同步获取图片,且获取成功后不会调用OnImageCallbackListener的onGetSuccess函数
(3)、设置缓存算法
setCacheFullRemoveType(CacheFullRemoveType<Bitmap> cacheFullRemoveType)
设置缓存算法,缓存算法即为缓存满时为了插入新数据,删除旧数据的规则。
目前包括FIFO、LIFO、LRU、MRU、LFU、MFU、优先级低先删除、优先级高先删除、数据小先删除、数据大先删除、图片小先删除、图片大先删除、永不删除。还可以通过实现CacheFullRemoveType来自定义缓存算法。。默认为RemoveTypeUsedCountSmall,即LRU使用频率低先删除。下面详细介绍各个算法:
RemoveTypeEnterTimeFirst FIFO先进先出,先进入先删除
RemoveTypeEnterTimeLast LIFO后进先出,后进入先删除
RemoveTypeLastUsedTimeFirst LRU(Least Recently User),最先使用先删除
RemoveTypeLastUsedTimeLast MRU(Most Recently Used),最近使用先删除
RemoveTypeUsedCountSmall LFU(Least Frequently Used),使用频率低先删除
RemoveTypeUsedCountBig MRU(Most Frequently Used),使用频率高先删除
RemoveTypePriorityLow 优先级低先删除
RemoveTypePriorityHigh 优先级低先删除
RemoveTypeBitmapSmall 图片小的先删除
RemoveTypeBitmapLarge 图片大的先删除
RemoveTypeDataBig 数据大先删除,根据缓存数据的compareTo函数决定
RemoveTypeDataSmall 数据小先删除,根据缓存数据的compareTo函数决定
RemoveTypeNotRemove 不删除,缓存满时不再允许插入新数据
自定义缓存算法只需要实现CacheFullRemoveType的compare方法即可。比较结果小于0表示会被先删除
1 2 3 4 5 6 7 8 9 |
public class RemoveTypePriorityHigh<T> implements CacheFullRemoveType<T> { private static final long serialVersionUID = 1L; @Override public int compare(CacheObject<T> obj1, CacheObject<T> obj2) { return (obj2.getPriority() > obj1.getPriority()) ? 1 : ((obj2.getPriority() == obj1.getPriority()) ? 0 : -1); } } |
(4)、保存及初始化恢复数据
public boolean saveDataToDb(Context context, String tag)
保存数据到数据库,可在程序退出时调用,不建议在每个activity onDestrory时调用,而是整个程序退出(比如”退出确认对话框”点击确认)时,见本文3.1常见问题解答。
public void initData(Context context, String tag)
初始化恢复数据,可在程序刚开始加载时调用,不建议在每个activity oncreate调用,而是整个程序初始化(比如Application的onCreate函数)时,见本文3.1常见问题解答。
(5)、是否启用队列
setOpenWaitingQueue(boolean isOpenWaitingQueue)
当不同view通过get函数获取图片时,是否开启等待队列。
若开启,保存所有view,图片获取成功后依次调用OnImageCallbackListener的onGetSuccess函数;否则仅保存最后调用get的view,图片获取成功后调用OnImageCallbackListener的onGetSuccess函数
默认开启队列等待。如果希望最优性能且场景满足,可设置为false。
(6)、设置图片获取方式接口
setOnGetDataListener(OnGetDataListener<String, Bitmap> onGetDataListener)
设置图片获取的方式。缓存通过该接口获取图片及预取图片。默认为ImageCache中getDefaultOnGetImageListener,通过url获取图片。你可以重写该方法将图片缓存改为本地图片文件缓存等等。
(7)、优化图片读取
a. 设置图片读取http超时
public void setHttpReadTimeOut(int readTimeOutMillis)
若readTimeOutMillis小于0表示不设置超时,默认不设置,单位为毫秒
b. 设置无网络不读取
public void setContext(Context context)
设置context,网络连接失败不会新建线程请求图片。
c. 支持不同网络类型的处理
public void setAllowedNetworkTypes(int allowedNetworkTypes)
设置允许的网络类型,可选择PreloadDataCache#NETWORK_MOBILE、PreloadDataCache#NETWORK_WIFI或两者都允许。默认两者都允许。
注意:这个接口生效必须先setContext(Context context)
d. 设置读取图片http请求属性
public void setRequestProperties(Map<String, String> requestProperties)
public void setRequestProperty(String field, String newValue)
设置获取图片时的http request属性,如
setRequestProperty(“User-Agent”, “TrineaAndroidCommon/4.0 (Android 4.0) MI_2S”);设置User-Agent头信息
setRequestProperty(“Connection”, “false”);设置connection keep alive为false,默认为true
(8)、缓存命中率
setCompressListener(CompressListener compressListener)
设置图片压缩比例,防止内存溢出
(9)、缓存元素有效时间
setValidTime(long validTime)
设置缓存元素有效时间,小于0表示不会失效,此时仅根据CacheFullRemoveType在缓存满时替换元素
通过protected boolean isExpired(K key)判断某key是否过期
(10)、缓存命中率
getHitRate()、getHitCount()、getMissCount()分别表示缓存命中率、命中次数、未命中次数
(11)、其他与map类似接口
public boolean containsKey(K key) 缓存中是否包含该key
public CacheObject<V> remove(K key) 从缓存中删除某个key
public void clear() 清空缓存
public Set<K> keySet() 缓存中key集合
public Set<Map.Entry<K, CacheObject<V>>> entrySet() 缓存中key,value键值对集合
public Collection<CacheObject<V>> values() 缓存中元素集合
public CacheObject<V> put(K key, V value) 手动插入某个元素
public CacheObject<V> put(K key, CacheObject<V> value) 手动插入某个元素
public CacheObject<V> get(K key) 得到某个key
public int getSize() 得到缓存中有效元素个数
public int getMaxSize() 得到缓存中元素最大个数
(12)得到设置
上面的set几乎都可以通过对应的get得到相应value
使用getOnGetDataListener()得到获取图片的方法
getForwardCacheNumber()得到自动向前缓存的个数
getBackCacheNumber()得到自动向后缓存的个数
getMaxSize()得到缓存最大容量
getValidTime()得到有效时间,以毫秒计
getCacheFullRemoveType()得到cache满时删除元素类型
3、常见问题解答
1、是每个Activity一个ImageCache还是整个程序共用一个?
建议ImageCache缓存整个app用同一个实例,而不是每个activity一个。在程序启动(比如Application的onCreate函数)时initData初始化图片缓存,退出(比如”退出确认对话框”点击确认)时saveDataToDb保存图片缓存。因为:
a. 你的程序总不能就一个activity需要图片缓存,缓存本身就该应该所有页面共用
b. 每次进activity新建一个缓存只会增加系统消耗,没有必要
c. 这个缓存不会占用太多内存,所以程序运行期间一直存在也不会对性能有多大影响
2、为什么应用退出后会重新获取图片?为什么应用退出后无网络情况下之前缓存的图片不会自己加载?
在程序退出(比如”退出确认对话框”点击确认)时调用saveDataToDb(Context context, String tag)保存数据,在程序启动(比如Application的onCreate函数)时调用initData(Context context, String tag)初始化数据。其中tag为此ImageCache的标识。
3、为什么无法加载图片?
可通过OnImageCallbackListener中的onGetFailed函数FailedReason参数判断失败原因。目前错误原因包括sdcard保存出错(请确保存在外部sdcard)和网络出错。
4、能否直接加载sdcard中已经存在的图片,即数据源为本地sdcard而不是网络?
可以通过setOnGetDataListener(OnGetDataListener<String, Bitmap> onGetDataListener)设置图片获取方式。如
1 |
setOnGetDataListener(ImageCacheManager.getImageFromSdcardListener()); |
注意这种方式compressListener是无效的,如果希望利用compressListener,可以如下设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
setOnGetDataListener(new OnGetDataListener<String, Bitmap>() { private static final long serialVersionUID = 1L; @Override public CacheObject<Bitmap> onGetData(String imagePath) { if (!FileUtils.isFileExist(imagePath)) { return null; } CompressListener compressListener = IMAGE_CACHE.getCompressListener(); int compressSize = 0; if (compressListener != null) { compressSize = compressListener.getCompressSize(imagePath); } Bitmap bm; if (compressSize > 1) { BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = compressSize; bm = BitmapFactory.decodeFile(imagePath, option); } else { bm = BitmapFactory.decodeFile(imagePath); } return (bm == null ? null : new CacheObject<Bitmap>(bm)); } }); |
5、内存溢出OOM问题?
通过setCompressListener接口压缩图片,getCompressSize返回值为图片长宽缩放比例,同BitmapFactory.Options#inSampleSize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
IMAGE_CACHE.setCompressListener(new CompressListener() { @Override public int getCompressSize(String imagePath) { if (FileUtils.isFileExist(imagePath)) { long fileSize = FileUtils.getFileSize(imagePath) / 1000; /** * if image bigger than 100k, compress to 1/(n + 1) width and 1/(n + 1) height, n is fileSize / 100k **/ if (fileSize > 100) { return (int)(fileSize / 100) + 1; } } return 1; } }); |
还可见 图片OutOfMemory异常bitmap size exceeds VM budget的原因及解决方法
5、其他
楼主,能帮我看下是什么原因吗?图片网址用了你的。你的可运行程序在我的机子上运行正常01-24 21:00:50.312: E/ImageSDCardCache(16097): get drawable exception, imageUrl is:http://farm8.staticflickr.com/7409/9148527822_36fa37d7ca_z.jpg01-24 21:00:50.312: E/ImageSDCardCache(16097): java.lang.RuntimeException: FileNotFoundException occurred. 01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.util.FileUtils.writeFile(FileUtils.java:178)01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.util.FileUtils.writeFile(FileUtils.java:141)01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.util.FileUtils.writeFile(FileUtils.java:128)01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.service.impl.ImageSDCardCache$3.onGetData(ImageSDCardCache.java:793)01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.service.impl.ImageSDCardCache$3.onGetData(ImageSDCardCache.java:1)01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.service.impl.PreloadDataCache$GetDataThread.run(PreloadDataCache.java:569)01-24 21:00:50.312: E/ImageSDCardCache(16097): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)01-24 21:00:50.312: E/ImageSDCardCache(16097): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)01-24 21:00:50.312: E/ImageSDCardCache(16097): at java.lang.Thread.run(Thread.java:856)01-24 21:00:50.312: E/ImageSDCardCache(16097): Caused by: java.io.FileNotFoundException: /storage/sdcard0/fruit/Data/ImageCache/http___farm8_staticflickr_com_7409_9148527822_36fa37d7ca_z_jpg: open failed: ENOENT (No such file or directory)01-24 21:00:50.312: E/ImageSDCardCache(16097): at libcore.io.IoBridge.open(IoBridge.java:416)01-24 21:00:50.312: E/ImageSDCardCache(16097): at java.io.FileOutputStream.(FileOutputStream.java:88)01-24 21:00:50.312: E/ImageSDCardCache(16097): at cn.trinea.android.common.util.FileUtils.writeFile(FileUtils.java:169)01-24 21:00:50.312: E/ImageSDCardCache(16097): … 8 more01-24 21:00:50.312: E/ImageSDCardCache(16097): Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)01-24 21:00:50.312: E/ImageSDCardCache(16097): at libcore.io.Posix.open(Native Method)01-24 21:00:50.312: E/ImageSDCardCache(16097): at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)01-24 21:00:50.312: E/ImageSDCardCache(16097): at libcore.io.IoBridge.open(IoBridge.java:400)01-24 21:00:50.312: E/ImageSDCardCache(16097): … 10 more01-24 21:00:50.312: E/imageCache(16097): get image http://farm8.staticflickr.com/7409/9148527822_36fa37d7ca_z.jpg error, failed type is: ERROR_NETWORK, failed reason is: get image from network error
网络错误
谢谢楼主的耐心指导,太感动了
请问楼主,一级缓存跟二级缓存是用什么实现的?
都是map,分别为ImageMemoryCache和ImageSDCardCache
有没有考虑用sparseArray?这个效率应该会高点,不过可能会失去一部分map具有的方法
十万以下量级用不着考虑太多,几十ns差别而已
java.lang.OutOfMemoryError 超过128M就溢出报错了 这段代码可以放在ImageCache的onImageLoaded或ImageSDCardCache的onImageLoaded中防止oom。但是ImageCache没有onImageLoaded这个函数
已经变成了onGetSuccess方法,回头我会修改下博客
public static final ImageCache IMAGE_CACHE = new ImageCache(128, 512);这个括号的两个参数分别是什么
分别为一级和二级缓存的大小
这两个大小是随便设置么
默认会根据系统配置决定的,不可能无限大,会内存溢出的
哦,按理来说我得到图片的链接和drawable对象之后不进行压缩也不会造成oom,是吧?
正常情况下是这样的
但是运行就报这句错, Drawable d = ImageUtils.bitmapToDrawable(BitmapFactory.decodeFile(imagePath));什么原因呢
为什么运行这句,在什么地方运行
他自动运行的,在你那个ImageCache类里面
能留个qq么,我向你请教一下
717763774,最近我比较忙,如果可以的话,本周末我会加上压缩的接口
能现在添加么,谢谢你啊,周五要交了!或者能说说压缩的思路么,谢谢你
告诉我你的需求,希望在多大图片时候压缩,还是希望所有图片压缩
希望全部图片控制在100k以内,小于100的就不用压缩,大于100的就压缩
重新拉下,已经添加接口setCompressSize的两种形式,setCompressSize(int) or setCompressSize(CompressListener)demo可见https://github.com/Trinea/AndroidDemo/blob/master/src/cn/trinea/android/demo/ImageCacheDemo.java#L48
谢谢,是需要从新引入公共库么才可以,是么?
然后还有一个问题就是你那个 public int getCompressSize(String imagePath) 这个imagePath是图片的链接吧,为什么我用我的链接不行,感觉这个imagePath是独立存在的
imagPath是图片存储路径,不是url,二级缓存会先将图片下载下来保存到sdcard,在一级缓存去读取时进行压缩
但是我不知道他的路径啊,我能拿到的只有他的url,在这个成功之后的回调方法public void onGetSuccess(String imageUrl, Drawable imageDrawable,View view, boolean isInCache) {}中拿不到他的路径,这块我该怎么处理
https://github.com/Trinea/AndroidDemo/blob/master/src/cn/trinea/android/demo/ImageCacheDemo.java#L48你有仔细看吗。。例子我都给你写好了,imagePath是ImageCache自己传过去的你不用管
哦,谢谢你,我再看看,
你这个用ImageCache 的时候,刚刚遇到一张2M的的图片一运行就直接包oom异常了,就是当遇到大图片的时候这块该怎么处理啊
如果我不设置是不是表示无限大?
12-27 17:30:30.690: E/ImageSDCardCache(25236): Caused by: java.io.FileNotFoundException: http://test001.cqhot.com/citybaike//uploads/1385005298.jpg我满篇都是这个问题,什么原因
http://test001.cqhot.com/citybaike//uploads/1385005298.jpg 这个图片不存在,获取错误,另外ListView的话用ImageCache而不要用ImageSdcardCache
用ImageCache的话怎么压缩图片呢
通过setOnGetImageListenerOfSecondaryCache设置。listener主要内容同https://github.com/Trinea/AndroidCommon/blob/master/src/cn/trinea/android/common/service/impl/ImageSDCardCache.java#L770,只是在InputStream stream = ImageUtils.getInputStreamFromUrl(key, httpReadTimeOut, requestProperties);后面处理下即可。大概半个月内这块我会开放个接口
这个方法看不当懂,我在onGetSuccess这个方法里面能够拿到imageDrawable,和imageUrl,我可以用这两个来自己压缩赛,但是不知道为什么我这里压缩不了
那个方法是获取图片的地方,只要改了那块所有地方都会生效
为什么我根据imageUrl这个图片的链接来压缩图片老是报空指针异常啊
为空说明图片获取失败,会走onGetFailed
为什么这个imageDrawable一直为空呢
介绍 (3). 包含二级缓存 这句话意思 是 内存缓存和SD卡 缓存吗? 我怎么看的源代码 ImageCache 只从内存缓存来取的,没有从Sd卡取. 我理解错了?
只是继承自ImageMemoryCache ,ImageMemoryCache 做为一级缓存,他的数据源是二级Sdcard缓存,见ImageSDCardCache secondaryCache
刚看了下git 上的ImageCache 类。 如果实现先从一级缓存取,如果没有则从二级SD卡取,若再没有,开启线程下载 这功能, 是不是要重写ImageMemoryCache 中的get(final String imageUrl, final List urlList, final View view) 方法。 我看了下这个方法。先从一级缓存取,没有就要开启线程去下载了。谢谢。
现在的ImageCache就已经实现了你说的方式,先从一级取,没有从sdcard取,再没有启动线程下载。见OnGetDataListener
嗯,刚才看了下,是这样的。谢谢。
新手提问,我同一个url换了张图片貌似不会更新只会用旧cache耶……有没有方法实现当cache中对应的key存在时判断一下url文件的大小是否符合cache中的文件大小然后决定是否更新这个cache?
缓存的机制就是一个url对应一个图片,并且按照http规范,图片修改建议用新的url。你说的判断新图片和老图片大小已经违背了缓存的定义,缓存就是说如果key已经存在就不会进行网络操作获取了
drowdownList下拉刷新的时候,数据加载完成listView.onDropDownComplete(“xxxxx”),有时候能够自动隐藏header,有时候不隐藏,而会显示”点击可以刷新,更新于xxxx”,这是什么原因?
onDropDownComplete恢复状态,注意需要在adapter.notifyDataSetChanged();后面调用
我是在MessageHandler,里面加载服务器返回的数据,然后调用adapter.notifyDataSetChanged();,最后调用SimpleDateFormat dateFormat = new SimpleDateFormat(“MM-dd HH:mm:ss”);listView.onDropDownComplete(getString(R.string.update_at) + dateFormat.format(new Date()));我同样的调用,加载不同的文章列表,区别就是文章的类型不一样,返回的列表文章数量不一样。同一个listview有时候header能自动隐藏,有时候不能隐藏显示“点击可以刷新”,但实际上listview中的内容变过来了。
不能隐藏是不是因为listView已经不能向下滚动了?
l两个不能隐藏的都是listview只有2条记录,一屏显示不满。这里怎么发截图,不然我可以发几张手机截图上去。
确实是有这个问题存在,目前也没有比较好的解决方法
有没有提供一个方法,可以强制list的header隐藏的。或者我自己能不能写一个方法让他隐藏?我在我数据加载成功的时候调用,让他强制隐藏掉。
getHeaderLayout(),setVisible应该也可以
你那个问题有个朋友开源的项目能很好解决 https://github.com/chrisbanes/Android-PullToRefresh 这个很强大,支持很多空间的上下左右拉刷新
楼主,我在activity退出时调用了IMAGE_CACHE.shutdown();然后再次打开这个activity就会报错java.util.concurrent.RejectedExecutionException: Task cn.trinea.android.common.service.impl.ImageSDCardCache$2@42cc34f0 rejected from java.util.concurrent.ThreadPoolExecutor@42591190[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 1]
这个接口你不用掉,后面我会废弃掉。调用了会导致线程池关闭再次进入时报错
图片正在加载能不能先显示自定义的正在加载的图片,等加载成功了显示加载到的图片。如果加载失败的话,回调很少在哪里设置?
现在默认图片可以这样设置if (!Cache.ICON_CACHE.get(imageUrl, appIcon)) { appIcon.setImageResource(R.drawable.icon);}失败的话还是没有回调接口的,后面会把OnImageCallbackListener扩展