android listview 加载图片错乱(错位)(2)

来源:互联网 发布:台湾会统一吗 知乎 编辑:程序博客网 时间:2024/05/07 08:36

 写道
今天晚上一个朋友介绍我看了一篇文章,也是解决android中listview在加载图片错位的问题,看了之后,感觉写的很好,自己也遇到这个问题,但是又不知道从何下手,看到这篇文章后,我的问题得到了解决,同时也感谢作者。
现在饿就把作者的文章转帖上来,给大家共享。

 

 

 

 写道
1、采用线程池

2、内存缓存+文件缓存

3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4

4、对下载的图片进行按比例缩放,以减少内存的消耗

具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:

 public class MemoryCache {

 

Java代码  收藏代码
  1. private static final String TAG = "MemoryCache";  
  2. // 放入缓存时是个同步操作  
  3. // LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU  
  4. // 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率  
  5. private Map<String, Bitmap> cache = Collections  
  6.                 .synchronizedMap(new LinkedHashMap<String, Bitmap>(101.5f, true));  
  7. // 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存  
  8. private long size = 0;// current allocated size  
  9. // 缓存只能占用的最大堆内存  
  10. private long limit = 1000000;// max memory in bytes  
  11.   
  12. public MemoryCache() {  
  13.         // use 25% of available heap size  
  14.         setLimit(Runtime.getRuntime().maxMemory() / 4);  
  15. }  
  16.   
  17. public void setLimit(long new_limit) {   
  18.         limit = new_limit;  
  19.         Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");  
  20. }  
  21.   
  22. public Bitmap get(String id) {  
  23.         try {  
  24.                 if (!cache.containsKey(id))  
  25.                         return null;  
  26.                 return cache.get(id);  
  27.         } catch (NullPointerException ex) {  
  28.                 return null;  
  29.         }  
  30. }  
  31.   
  32. public void put(String id, Bitmap bitmap) {  
  33.         try {  
  34.                 if (cache.containsKey(id))  
  35.                         size -= getSizeInBytes(cache.get(id));  
  36.                 cache.put(id, bitmap);  
  37.                 size += getSizeInBytes(bitmap);  
  38.                 checkSize();  
  39.         } catch (Throwable th) {  
  40.                 th.printStackTrace();  
  41.         }  
  42. }  
  43.   
  44. /** 
  45.  * 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存 
  46.  *  
  47.  */  
  48. private void checkSize() {  
  49.         Log.i(TAG, "cache size=" + size + " length=" + cache.size());  
  50.         if (size > limit) {  
  51.                 // 先遍历最近最少使用的元素  
  52.                 Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();  
  53.                 while (iter.hasNext()) {  
  54.                         Entry<String, Bitmap> entry = iter.next();  
  55.                         size -= getSizeInBytes(entry.getValue());  
  56.                         iter.remove();  
  57.                         if (size <= limit)  
  58.                                 break;  
  59.                 }  
  60.                 Log.i(TAG, "Clean cache. New size " + cache.size());  
  61.         }  
  62. }  
  63.   
  64. public void clear() {  
  65.         cache.clear();  
  66. }  
  67.   
  68. /** 
  69.  * 图片占用的内存 
  70.  *  
  71.  * @param bitmap 
  72.  * @return 
  73.  */  
  74. long getSizeInBytes(Bitmap bitmap) {  
  75.         if (bitmap == null)  
  76.                 return 0;  
  77.         return bitmap.getRowBytes() * bitmap.getHeight();  
  78. }  

 

 

 

Java代码  收藏代码
  1. 也可以使用SoftReference,代码会简单很多,但是推荐上面的方法。  
  2.   
  3. public class MemoryCache {  
  4.           
  5.         private Map<String, SoftReference<Bitmap>> cache = Collections  
  6.                         .synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());  
  7.   
  8.         public Bitmap get(String id) {  
  9.                 if (!cache.containsKey(id))  
  10.                         return null;  
  11.                 SoftReference<Bitmap> ref = cache.get(id);  
  12.                 return ref.get();  
  13.         }  
  14.   
  15.         public void put(String id, Bitmap bitmap) {  
  16.                 cache.put(id, new SoftReference<Bitmap>(bitmap));  
  17.         }  
  18.   
  19.         public void clear() {  
  20.                 cache.clear();  
  21.         }  
  22.   
  23. }  

 

 

 

Java代码  收藏代码
  1. 下面是文件缓存类的代码FileCache.java  
  2.   
  3. public class FileCache {  
  4.   
  5.         private File cacheDir;  
  6.   
  7.         public FileCache(Context context) {  
  8.                 // 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片  
  9.                 // 没有SD卡就放在系统的缓存目录中  
  10.                 if (android.os.Environment.getExternalStorageState().equals(  
  11.                                 android.os.Environment.MEDIA_MOUNTED))  
  12.                         cacheDir = new File(  
  13.                                         android.os.Environment.getExternalStorageDirectory(),  
  14.                                         "LazyList");  
  15.                 else  
  16.                         cacheDir = context.getCacheDir();  
  17.                 if (!cacheDir.exists())  
  18.                         cacheDir.mkdirs();  
  19.         }  
  20.   
  21.         public File getFile(String url) {  
  22.                 // 将url的hashCode作为缓存的文件名  
  23.                 String filename = String.valueOf(url.hashCode());  
  24.                 // Another possible solution  
  25.                 // String filename = URLEncoder.encode(url);  
  26.                 File f = new File(cacheDir, filename);  
  27.                 return f;  
  28.   
  29.         }  
  30.   
  31.         public void clear() {  
  32.                 File[] files = cacheDir.listFiles();  
  33.                 if (files == null)  
  34.                         return;  
  35.                 for (File f : files)  
  36.                         f.delete();  
  37.         }  
  38.   
  39. }  

 最后最重要的加载图片的类,ImageLoader.java

 

Java代码  收藏代码
  1. public class ImageLoader {  
  2.   
  3.         MemoryCache memoryCache = new MemoryCache();  
  4.         FileCache fileCache;  
  5.         private Map<ImageView, String> imageViews = Collections  
  6.                         .synchronizedMap(new WeakHashMap<ImageView, String>());  
  7.         // 线程池  
  8.         ExecutorService executorService;  
  9.   
  10.         public ImageLoader(Context context) {  
  11.                 fileCache = new FileCache(context);  
  12.                 executorService = Executors.newFixedThreadPool(5);  
  13.         }  
  14.   
  15.         // 当进入listview时默认的图片,可换成你自己的默认图片  
  16.         final int stub_id = R.drawable.stub;  
  17.   
  18.         // 最主要的方法  
  19.         public void DisplayImage(String url, ImageView imageView) {  
  20.                 imageViews.put(imageView, url);  
  21.                 // 先从内存缓存中查找  
  22.   
  23.                 Bitmap bitmap = memoryCache.get(url);  
  24.                 if (bitmap != null)  
  25.                         imageView.setImageBitmap(bitmap);  
  26.                 else {  
  27.                         // 若没有的话则开启新线程加载图片  
  28.                         queuePhoto(url, imageView);  
  29.                         imageView.setImageResource(stub_id);  
  30.                 }  
  31.         }  
  32.   
  33.         private void queuePhoto(String url, ImageView imageView) {  
  34.                 PhotoToLoad p = new PhotoToLoad(url, imageView);  
  35.                 executorService.submit(new PhotosLoader(p));  
  36.         }  
  37.   
  38.         private Bitmap getBitmap(String url) {  
  39.                 File f = fileCache.getFile(url);  
  40.   
  41.                 // 先从文件缓存中查找是否有  
  42.                 Bitmap b = decodeFile(f);  
  43.                 if (b != null)  
  44.                         return b;  
  45.   
  46.                 // 最后从指定的url中下载图片  
  47.                 try {  
  48.                         Bitmap bitmap = null;  
  49.                         URL imageUrl = new URL(url);  
  50.                         HttpURLConnection conn = (HttpURLConnection) imageUrl  
  51.                                         .openConnection();  
  52.                         conn.setConnectTimeout(30000);  
  53.                         conn.setReadTimeout(30000);  
  54.                         conn.setInstanceFollowRedirects(true);  
  55.                         InputStream is = conn.getInputStream();  
  56.                         OutputStream os = new FileOutputStream(f);  
  57.                         CopyStream(is, os);  
  58.                         os.close();  
  59.                         bitmap = decodeFile(f);  
  60.                         return bitmap;  
  61.                 } catch (Exception ex) {  
  62.                         ex.printStackTrace();  
  63.                         return null;  
  64.                 }  
  65.         }  
  66.   
  67.         // decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的  
  68.         private Bitmap decodeFile(File f) {  
  69.                 try {  
  70.                         // decode image size  
  71.                         BitmapFactory.Options o = new BitmapFactory.Options();  
  72.                         o.inJustDecodeBounds = true;  
  73.                         BitmapFactory.decodeStream(new FileInputStream(f), null, o);  
  74.   
  75.                         // Find the correct scale value. It should be the power of 2.  
  76.                         final int REQUIRED_SIZE = 70;  
  77.                         int width_tmp = o.outWidth, height_tmp = o.outHeight;  
  78.                         int scale = 1;  
  79.                         while (true) {  
  80.                                 if (width_tmp / 2 < REQUIRED_SIZE  
  81.                                                 || height_tmp / 2 < REQUIRED_SIZE)  
  82.                                         break;  
  83.                                 width_tmp /= 2;  
  84.                                 height_tmp /= 2;  
  85.                                 scale *= 2;  
  86.                         }  
  87.   
  88.                         // decode with inSampleSize  
  89.                         BitmapFactory.Options o2 = new BitmapFactory.Options();  
  90.                         o2.inSampleSize = scale;  
  91.                         return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);  
  92.                 } catch (FileNotFoundException e) {  
  93.                 }  
  94.                 return null;  
  95.         }  
  96.   
  97.         // Task for the queue  
  98.         private class PhotoToLoad {  
  99.                 public String url;  
  100.                 public ImageView imageView;  
  101.   
  102.                 public PhotoToLoad(String u, ImageView i) {  
  103.                         url = u;  
  104.                         imageView = i;  
  105.                 }  
  106.         }  
  107.   
  108.         class PhotosLoader implements Runnable {  
  109.                 PhotoToLoad photoToLoad;  
  110.   
  111.                 PhotosLoader(PhotoToLoad photoToLoad) {  
  112.                         this.photoToLoad = photoToLoad;  
  113.                 }  
  114.   
  115.                 @Override  
  116.                 public void run() {  
  117.                         if (imageViewReused(photoToLoad))  
  118.                                 return;  
  119.                         Bitmap bmp = getBitmap(photoToLoad.url);  
  120.                         memoryCache.put(photoToLoad.url, bmp);  
  121.                         if (imageViewReused(photoToLoad))  
  122.                                 return;  
  123.                         BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);  
  124.                         // 更新的操作放在UI线程中  
  125.                         Activity a = (Activity) photoToLoad.imageView.getContext();  
  126.                         a.runOnUiThread(bd);  
  127.                 }  
  128.         }  
  129.   
  130.         /** 
  131.          * 防止图片错位 
  132.          *  
  133.          * @param photoToLoad 
  134.          * @return 
  135.          */  
  136.         boolean imageViewReused(PhotoToLoad photoToLoad) {  
  137.                 String tag = imageViews.get(photoToLoad.imageView);  
  138.                 if (tag == null || !tag.equals(photoToLoad.url))  
  139.                         return true;  
  140.                 return false;  
  141.         }  
  142.   
  143.         // 用于在UI线程中更新界面  
  144.         class BitmapDisplayer implements Runnable {  
  145.                 Bitmap bitmap;  
  146.                 PhotoToLoad photoToLoad;  
  147.   
  148.                 public BitmapDisplayer(Bitmap b, PhotoToLoad p) {  
  149.                         bitmap = b;  
  150.                         photoToLoad = p;  
  151.                 }  
  152.   
  153.                 public void run() {  
  154.                         if (imageViewReused(photoToLoad))  
  155.                                 return;  
  156.                         if (bitmap != null)  
  157.                                 photoToLoad.imageView.setImageBitmap(bitmap);  
  158.                         else  
  159.                                 photoToLoad.imageView.setImageResource(stub_id);  
  160.                 }  
  161.         }  
  162.   
  163.         public void clearCache() {  
  164.                 memoryCache.clear();  
  165.                 fileCache.clear();  
  166.         }  
  167.   
  168.         public static void CopyStream(InputStream is, OutputStream os) {  
  169.                 final int buffer_size = 1024;  
  170.                 try {  
  171.                         byte[] bytes = new byte[buffer_size];  
  172.                         for (;;) {  
  173.                                 int count = is.read(bytes, 0, buffer_size);  
  174.                                 if (count == -1)  
  175.                                         break;  
  176.                                 os.write(bytes, 0, count);  
  177.                         }  
  178.                 } catch (Exception ex) {  
  179.                 }  
  180.         }  
  181. }  

 写道

主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。

 

 

Java代码  收藏代码
  1. a.runOnUiThread(...);  
 

 

 

Java代码  收藏代码
  1. 在你的程序中的基本用法:  
  2.   
  3. ImageLoader imageLoader=new ImageLoader(context);  
  4. ...  
  5. imageLoader.DisplayImage(url, imageView);  
  6.   
  7. 比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。  
Java代码  收藏代码
  1. adapter的代码:  

 

 

Java代码  收藏代码
  1. class MyAdapter extends BaseAdapter{  
  2.   
  3.         private String urls[];  
  4.         private Context context;  
  5.           
  6.         public int getCount() {  
  7.             return urls.length;  
  8.         }  
  9.   
  10.         public void setData(String[] urls) {  
  11.             this.urls = urls;  
  12.         }  
  13.   
  14.         public Object getItem(int position) {  
  15.             return urls[position];  
  16.         }  
  17.   
  18.         public long getItemId(int position) {  
  19.             return position;  
  20.         }  
  21.   
  22.         public View getView(int position, View convertView, ViewGroup parent) {  
  23.             ViewHolder holder = null;  
  24.             if(convertView == null){  
  25.                 holder = new ViewHolder();  
  26.                 convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.listview_item, null);  
  27.                 holder.imageView = (ImageView) convertView.findViewById(R.id.imageview);  
  28.                 convertView.setTag(holder);  
  29.             }else{  
  30.                 holder = (ViewHolder) convertView.getTag();  
  31.             }  
  32.             System.out.println("开始下载图片 --------------position--------==== " + position);  
  33.             //把imageLoader传进adapter里面来  
  34.             imageLoader.displayImage(urls[position], holder.imageView);  
  35.               
  36.             return convertView;  
  37.         }  
  38.           
  39.         class ViewHolder{  
  40.             ImageView imageView;  
  41.         }  
  42.           
  43.     }  
  44.       
 写道
最后注意一点:要加权限。网络,sdcard等权限。
  • testlistview.rar (183.3 KB)
  • 下载次数: 155
分享到:  
android 图片缩放 | java 排序
  • 2012-07-27 22:35
  • 浏览 2924
  • 评论(5)
  • 分类:移动开发
  • 相关推荐
评论
5 楼 410812571 2013-06-04  
你就是救世主。
4 楼 410812571 2013-06-04  
你是救苦救难的活菩萨啊
3 楼 410812571 2013-06-04  
你是救苦救难的活菩萨啊
2 楼 tq09931 2012-08-27  
你好,发现你的代码有个小问题,就是会卡在PhotosLoader 的run方法中,导致无法显示图片,可能是线程导致,改成handler后就正常了 
Java代码  收藏代码
  1. class PhotosLoader implements Runnable {  
  2.             PhotoToLoad photoToLoad;  
  3.   
  4.             PhotosLoader(PhotoToLoad photoToLoad) {  
  5.                     this.photoToLoad = photoToLoad;  
  6.             }  
  7.   
  8.             public void run() {  
  9.                     if (imageViewReused(photoToLoad))  
  10.                             return;  
  11.                     Bitmap bmp = getBitmap(photoToLoad.url);  
  12.                     memoryCache.put(photoToLoad.url, bmp);  
  13.                     if (imageViewReused(photoToLoad))  
  14.                             return;  
  15.                     BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);  
  16.                     // 更新的操作放在UI线程中  
  17. //原先你的代码发现会开在这一步  你原来用的是Activity 的ui线程来执行的,不知道为什么会卡住,你知道为什么吗?如知道请告知,谢谢~  
  18.                     handler.post(bd);  
  19.                     System.out.println("zhixing shezhi");  
  20.             }  
  21.     }  
1 楼 a379933101 2012-08-22  
高手呀,不知道怎么感谢你好,所以就不感谢了!
0 0
原创粉丝点击