Android ListView和GridView异步加载图片
2015-10-07 22:53
330 查看
Android ListView和GridView异步加载图片
一般大家在加载图片的时候都是从网络或者SD卡硬盘上,这种操作是耗时操作,是不能在UI线程进行的所以需要异步加载图片,这样我们就会用到Android的异步任务AsyncTask类。不会使用这个的可以去网上百度。但是当我们使用ListView的时候,加载Item里面的图片就会出问题了。因为ListView的item是随着我们手指滑动,item的在屏幕上的显示和消失而回收利用。举个例子:加入现在我们的屏幕最多可以显示8个listview,当我们向上滑动的时候,第一个Item会消失,这个item不会被垃圾回收器回收,而是被用来显示第九个Item(这样我们就可以减少创建Item对象的开销),当第一个图片出现的时候会打开一个异步任务我们暂且就叫他TASK1,在TASK1还未加载完成的时候我们将ListView向下滑这个时候Item1消失被用来显示Item9,这个时候又启动了一个任务TASK9,这两个Item使用的是同一个View对象那么就会同时有两个TASK在操纵这个对象。这样就有可能在Item9上显示Item1的图片,在Item1上显示Item9的图片。也就是我们开发中可能遇到的ListView图片错乱。
针对这个问题,官方文档给了一种解决办法大致就是下面这样:
Created with Raphaël 2.1.0Item显示将Item的ImageView和Task绑定,并开始进行异步加载用户手指滑动使得旧的Item划出屏幕新的Item进入屏幕检查新的Item所使用的View是否已经开启了一个Task如果已经开启,关闭旧的Task,开启新的Task并和Item的Imageview绑定End
这样就不会出现ListView图片乱序的问题了。
下面我们看一下官方文档给出的代码(删去了一些关系不大的代码)。
class BitmapWorkerTask extends AsyncTask<URL, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private URL data = null; public BitmapWorkerTask(ImageView imageView) { imageViewReference = new WeakReference<ImageView>(imageView); } @Override protected Bitmap doInBackground(URL... params) { data = params[0]; return ImageUtils.getBitmap(data); } @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } }}
这段代码是一个异步加载图片的任务。
static class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); }}
这里复写了一个Drawable类AsyncDrawable,我们可以在它的构造函数里面传入异步加载任务bitmapWorkerTask。
public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); }}
这里new出来一个BitmapWorkerTask,然后把BitmapWorkerTask放到上面的AsyncDrawable对象里面然后调用imageView.setImageDrawable(asyncDrawable);这样ImageView就和BitmapWorkerTask绑定到一起了,然后开始执行Task也就是开始加载图片。
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final URL bitmapData = bitmapWorkerTask.data; if (bitmapData == 0 || bitmapData != data) { bitmapWorkerTask.cancel(true); } else { return false; } } return true;}
这段代码是上面那段代码的一函数,如果现在已经有了一个Task并且和我们将要执行的那个Task不一样的话,我们就取消原来的Task。
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } }
最后在Task执行完毕之后,在onPostExecute里面得到Bitmap并且设置到imageView里面就好了。如果看不懂的话就再看几遍上面的流程图,流程图就是全部过程
最后总结一下:把View比作一个人的话,Item1就是他白天的职业——软件工程师,Item9就是他晚上的职业——出租车司机。白天的时候只敲代码(Task1),晚上的时候他就是出租车司机了,脑子里不去想代码(关闭Task1)开始集中精力开车(开启Task9)。这样他就不会感到职业混乱啦~~你是不是也明白了呢 ^_^。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories