android listview 异步加载图片并防止错位
網上找了一張圖, listview 異步加載圖片之所以錯位的根本原因是重用了?convertView 且有異步操作.
如果不重用?convertView 不會出現錯位現象, 重用?convertView 但沒有異步操作也不會有問題。
我簡單分析一下:
當重用?convertView 時,最初一屏顯示 7 條記錄, getView 被調用 7 次,創建了 7 個?convertView.
當 Item1 劃出屏幕, Item8 進入屏幕時,這時沒有為 Item8 創建新的 view 實例, Item8 復用的是
Item1 的?view?如果沒有異步不會有任何問題,雖然?Item8 和 Item1 指向的是同一個 view,但滑到
Item8 時刷上了 Item8 的數據,這時 Item1?的數據和 Item8 是一樣的,因為它們指向的是同一塊內存,
但 Item1 已滾出了屏幕你看不見。當 Item1 再次可見時這塊 view 又涮上了 Item1 的數據。
?
但當有異步下載時就有問題了,假設 Item1 的圖片下載的比較慢,Item8 的圖片下載的比較快,你滾上去
使 Item8 可見,這時 Item8 先顯示它自己下載的圖片沒錯,但等到 Item1 的圖片也下載完時你發現
Item8 的圖片也變成了 Item1 的圖片,因為它們復用的是同一個 view。 如果 Item1 的圖片下載的比
Item8 的圖片快, Item1 先刷上自己下載的圖片,這時你滑下去,Item8 的圖片還沒下載完, Item8
會先顯示 Item1 的圖片,因為它們是同一快內存,當?Item8 自己的圖片下載完后 Item8 的圖片又刷成
了自己的,你再滑上去使 Item1 可見, Item1 的圖片也會和 Item8 的圖片是一樣的,
因為它們指向的是同一塊內存。
?
最簡單的解決方法就是網上說的,給 ImageView 設置一個 tag, 并預設一個圖片。
當 Item1 比 Item8 圖片下載的快時, 你滾下去使 Item8 可見,這時 ImageView 的 tag 被設成了
Item8 的 URL, 當 Item1 下載完時,由于 Item1 不可見現在的 tag 是 Item8 的 URL,所以不滿足條件,
雖然下載下來了但不會設置到 ImageView 上, tag 標識的永遠是可見 view 中圖片的 URL。
關鍵代碼如下:
public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {holder = new ViewHolder();convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null);holder.img = (ImageView) convertView.findViewById(R.id.userimage);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}User user = list.get(position);// 給 ImageView 設置一個 tagholder.img.setTag(user.getImgUrl());// 預設一個圖片holder.img.setImageResource(R.drawable.ic_launcher);final String tmpImageUrl = user.getImgUrl();if (user.getImgUrl() != null && !user.getImgUrl().equals("")) {Bitmap bitmap = imageLoader.loadImage(holder.img, user.getImgUrl(),new ImageDownloadCallBack() {@Overridepublic void onImageDownloaded(ImageView imageView,Bitmap bitmap) {// 通過 tag 來防止圖片錯位if (imageView.getTag() != null&& imageView.getTag().equals(tmpImageUrl)) {imageView.setImageBitmap(bitmap);}}});if (bitmap != null) {holder.img.setImageBitmap(bitmap);}}return convertView; }我參考網上資料寫了一個 listview 異步加載圖片的 DEMO:
(1) 使用線程池
?沒有線程池,當圖片非常多,快速滑動 ?listview 時由于下載每個圖片都開了一個線程,
?可能出現 OOM (out of memory)。
(2) 內存、文件雙緩存
?這里也使用 SoftReference 軟引用
/*** 圖片異步加載類* * @author Leslie.Fang* @company EnwaySoft* */ public class AsyncImageLoader {// 最大線程數private static final int MAX_THREAD_NUM = 10;private Map<String, SoftReference<Bitmap>> imageCaches = null;private FileUtil fileUtil;// 線程池private ExecutorService threadPools = null;public AsyncImageLoader(Context context) {imageCaches = new HashMap<String, SoftReference<Bitmap>>();fileUtil = new FileUtil(context);}public Bitmap loadImage(final ImageView imageView, final String imageUrl,final ImageDownloadCallBack imageDownloadCallBack) {final String filename = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);final String filepath = fileUtil.getAbsolutePath() + "/" + filename;// 先從軟引用中找if (imageCaches.containsKey(imageUrl)) {SoftReference<Bitmap> reference = imageCaches.get(imageUrl);Bitmap bitmap = reference.get();// 軟引用中的 Bitmap 對象可能隨時被回收// 如果軟引用中的 Bitmap 已被回收,則從文件中找if (bitmap != null) {Log.i("aaaa", "cache exists " + filename);return bitmap;}}// 從文件中找if (fileUtil.isBitmapExists(filename)) {Log.i("aaaa", "file exists " + filename);Bitmap bitmap = BitmapFactory.decodeFile(filepath);// 重新加入到內存軟引用中imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));return bitmap;}// 軟引用和文件中都沒有再從網絡下載if (imageUrl != null && !imageUrl.equals("")) {if (threadPools == null) {threadPools = Executors.newFixedThreadPool(MAX_THREAD_NUM);}final Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 111 && imageDownloadCallBack != null) {Bitmap bitmap = (Bitmap) msg.obj;imageDownloadCallBack.onImageDownloaded(imageView,bitmap);}}};Thread thread = new Thread() {@Overridepublic void run() {Log.i("aaaa", Thread.currentThread().getName()+ " is running");InputStream inputStream = HTTPService.getInstance().getStream(imageUrl);Bitmap bitmap = BitmapFactory.decodeStream(inputStream);// 圖片下載成功重新緩存并執行回調刷新界面if (bitmap != null) {// 加入到軟引用中imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));// 緩存到文件系統fileUtil.saveBitmap(filepath, bitmap);Message msg = new Message();msg.what = 111;msg.obj = bitmap;handler.sendMessage(msg);}}};threadPools.execute(thread);}return null;}public void shutDownThreadPool() {if (threadPools != null) {threadPools.shutdown();threadPools = null;}}/*** 圖片下載完成回調接口* */public interface ImageDownloadCallBack {void onImageDownloaded(ImageView imageView, Bitmap bitmap);} }??DEMO 下載地址:http://pan.baidu.com/s/1sjttuFj
博客轉載自:
http://www.cnblogs.com/lesliefang/p/3619223.html
?
轉載于:https://www.cnblogs.com/ycxyyzw/p/3813743.html
總結
以上是生活随笔為你收集整理的android listview 异步加载图片并防止错位的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 四叶草efi_四叶草启动器Clover
- 下一篇: Redis数据结构