安卓ListView图片异步加载错位的处理
2015-09-21 13:04
357 查看
处理方法
我们知道了产生问题出在复用convertview时候。convertview中,我们的Imagview控件的数量是有限的,但是获取到的url却是很多的。我们需要明确当前的ImageView中用到的是哪张图片。图片的获取是异步的,这个时间差让我们不容易根据图片本身来判断其应该放在哪里。
我觉得对于这种异步线程问题的处理,我们必须找到一些标志,就像网上很多资料说的加tag,但是具体tag怎么加,加到哪里呢?
首先明确我们的imageview数量是固定的,当imageview显示的时候,有一张图片和它对应,而一个图片又对应一个url地址。也就是说,imageview在显示的时候,其实可以对应一个图片url地址。当其移出屏幕时,由于我们对convertview的复用,这个时候imageview必须对应另外一个url,但是此时我们的异步任务还在为这个imageview加载上一个url中的图片。这样就可能出现乱序的或者闪烁的问题。
可见,我们必须明确imageview中,当前应该对应哪一个url。这个tag也就是应该设置给imagview,而tag的内容就是url,这样就建立起了imagview和url的关系,当我们发现下载图片使用的url和imageview中存储的url不是一个值的时候,说明convertview已经复用,改变了imageview中的tag。此时图片和iamgeview已经不对应了,如果显示这张图片,就会造成错位。
自建异步图片加载类
/** * 图片异步加载类 * @author vonchenchen * */ public class AsyncImageLoader { private static final boolean DEBUG = true; private static final String TAG = "AsyncImageLoader"; /** 创建一个线程池 */ private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); final Handler handler = new Handler(); private LruCache<String, Bitmap> imageCache; public AsyncImageLoader() { //获取当前程序可用内存大小 int maxMemory = (int)(Runtime.getRuntime().maxMemory()/1024); // 计算缓存大小 int cacheSize = maxMemory/8; imageCache = new LruCache<String, Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount()/1024; } }; } /** * 获取图片成功后回调接口 */ public interface ImageViewCallback { public void onImageLoad(ImageView iv, Bitmap Bitmap, String url); public void onError(String t); } /** * 开启线程,加载图片 * * 图片控件需要与对应的url绑定,防止在listview中使用时由于convertview复用而出错 * @param iv 图片控件 * @param imageUrl 图片url * @param imageViewCallback * @return */ public Drawable loadDrawable(final ImageView iv, final String imageUrl, final ImageViewCallback imageViewCallback) { //将当前url和当前图片控件绑定 iv.setTag(imageUrl); fixedThreadPool.execute(new Runnable() { @Override public void run() { loadImg(iv, imageUrl, imageViewCallback); } }); return null; } /** * 根据url加载图片 * @param url * @return * @throws IOException */ public static Bitmap loadImageFromUrl(String url) throws IOException { URL m; InputStream i = null; m = new URL(url); i = (InputStream) m.getContent(); Drawable d = Drawable.createFromStream(i, "src"); BitmapDrawable bd = (BitmapDrawable) d; Bitmap bm = bd.getBitmap(); return bm; } /** * 加载图片 先从lru缓存中寻找图片 如果lru中的图片被回收,则请求网络 * @param iv * @param imageUrl * @param imageViewCallback */ private void loadImg(final ImageView iv, final String imageUrl, final ImageViewCallback imageViewCallback) { if (imageCache.get(imageUrl) != null) { Log.i(TAG, "**************catch*****************"); Bitmap bitmap = imageCache.get(imageUrl); final Bitmap bitmapTmp = bitmap; if (bitmapTmp != null) { //放到主线程执行ui操作 handler.post(new Runnable() { @Override public void run() { //imageViewCallback.onImageLoad(iv, bitmapTmp, imageUrl); setImageView(iv, bitmapTmp, imageUrl); } }); return; } }else{ // 尝试从URL中加载 try { Log.i(TAG, "**************url*****************"+imageUrl); final Bitmap bitmap = loadImageFromUrl(imageUrl); //将图片存入lru缓存中 if (bitmap != null) { imageCache.put(imageUrl, bitmap); } handler.post(new Runnable() { @Override public void run() { setImageView(iv, bitmap, imageUrl); } }); } catch (IOException e) { e.printStackTrace(); } } } private void setImageView(ImageView iv, Bitmap bitmap, String url){ //确保当前图片加载控件是显示控件 (而不是复用了原先的) //当前图片控件对应的是这个图片控件中应该使用的url才加载 //如果convertview被复用,iv中的tag会变为其他值,和原先url不匹配,则不加载这个图片 if(iv.getTag().equals(url)){ iv.setBackgroundColor(0x00000000); iv.setImageBitmap(bitmap); } } }
具体思路是
1.建立一块lru缓存
2.建立一个线程池
3.调用loadDrawable 传入控件和图片url,绑定imageview控件和url的关系,开启线程。先寻找lrucache中是否有图片内容,url中没有则从网络获取,使用drawable的createFromStream方法从网络获取图片。
4.判断下载图片使用的url与imagview中tag中保存的是否为一个值,如果是,说明当前imagview加载的是其需要的图片,可以加载,否则加载的是乱序的图片,跳过不去加载。
调用方法
我们以在baseAdapter中的getView方法中为例。
public View getView(int position, View convertView, ViewGroup parent) { ListItem item = getItem(position); ViewHolder viewHolder = null; if(convertView == null){ convertView = LayoutInflater.from(getContext()).inflate(myResourceId, null); viewHolder = new ViewHolder(); viewHolder.appLogo = (ImageView) convertView.findViewById(R.id.iv); convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } asyncImageLoader.loadDrawable(viewHolder.appLogo,item.getImgUrl(), null); return convertView; } class ViewHolder{ ImageView appLogo; }
相关文章推荐
- Gray码的生成算法(直接由二进制数转格雷码)
- 让php支持yar.packager实现文字高亮的效果
- MFC对话框应用程序 如何在对话框窗口显示前获取窗口(控件)的屏幕坐标位置
- OCP-V13-703
- opencv鼠标画矩形截取保存图片一部分并归一化为原图大小(Patches selected for feature extraction)
- PHP几个防SQL注入攻击自带函数区别 .
- Thinkpad X240修改bios引导方式
- ubuntu 网络配置
- tableview里面数据cell的个数居然不能改了(section footer )
- 对git的认识
- 服务器文件拷贝,移动
- 2013.10-2015.9 图书馆借阅书籍情况
- SSL&TLS介绍及差异
- 23设计模式之装饰模式(Decorator)
- iOS集成支付宝
- 问题:DataGrid该行并不总是很清楚验证错误(删除), 解决方案,如下面
- STL - C++ 11的Lambda表达式(下)
- 提高数据库效率
- 解决vsftpd 530 Permission denied报错
- Mac iOS推送测试