从相册中选择多张图片

来源:互联网 发布:济南好玩的地方 知乎 编辑:程序博客网 时间:2024/04/30 19:08

多选相册的实现,会涉及到下面几个问题:  

1、  取得图片路径:这里涉及到contentprovider,不是本文重点,这里只提供代码,不做详细解释。

2、  耗时操作:相册图片的读取是从硬盘读取的,这是一个耗时操作,不能直接在ui主线程操作,应该另起线程,可以使用AsyncTask来加载图片。

3、  并发性:相册有大量图片,通常我们用gridview来显示,同时会用viewholder来复用view,但是由于我们的图片加载是在线程中并发操作的,快速滑动gridview时,会使得同一个view,同时有多个task在加载图片,会导致图片错位和view一直在变换图片,而且图片加载效率非常低(如果没看明白,不用着急,下面有例子展示)。

4、  图片缓存:为了提高效率,我们应该对图片做缓存,加载图片时,先从缓存读取,读取不到再去硬盘读取。一方面内存读取效率高,另一方面减少重复操作(硬盘读取时,我们是先做压缩,再读取)。

 下面一一解决上面的问题。

1、取得相册图片路径。

[java] view plain copy
  1. public static List<ImageModel> getImages(Context context){  
  2.     List<ImageModel> list = new ArrayList<ImageModel>();  
  3.     ContentResolver contentResolver = context.getContentResolver();  
  4.     Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;  
  5.     String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA,};  
  6.     String sortOrder = MediaStore.Images.Media.DATE_ADDED + " desc";  
  7.     Cursor cursor = contentResolver.query(uri, projection, nullnull, sortOrder);  
  8.     int iId = cursor.getColumnIndex(MediaStore.Images.Media._ID);  
  9.     int iPath = cursor.getColumnIndex(MediaStore.Images.Media.DATA);  
  10.     cursor.moveToFirst();  
  11.     while (!cursor.isAfterLast()) {  
  12.         String id = cursor.getString(iId);  
  13.         String path = cursor.getString(iPath);  
  14.         ImageModel imageModel = new ImageModel(id,path);  
  15.         list.add(imageModel);  
  16.         cursor.moveToNext();  
  17.     }  
  18.     cursor.close();  
  19.     return list;  
  20. }<strong>  
  21. </strong>  

其中ImageModel为图片类:

[java] view plain copy
  1. public class ImageModel {  
  2.   
  3.     private String id;//图片id  
  4.     private String path;//路径  
  5.     private Boolean isChecked = false;//是否被选中  
  6.   
  7.     public ImageModel(String id, String path, Boolean isChecked) {  
  8.         this.id = id;  
  9.         this.path = path;  
  10.         this.isChecked = isChecked;  
  11.     }  
  12.   
  13.     public ImageModel(String id, String path) {  
  14.         this.id = id;  
  15.         this.path = path;  
  16.     }  
  17.   
  18.     public String getId() {  
  19.         return id;  
  20.     }  
  21.   
  22.     public void setId(String id) {  
  23.         this.id = id;  
  24.     }  
  25.   
  26.     public String getPath() {  
  27.         return path;  
  28.     }  
  29.   
  30.     public void setPath(String path) {  
  31.         this.path = path;  
  32.     }  
  33.   
  34.     public Boolean getIsChecked() {  
  35.         return isChecked;  
  36.     }  
  37.   
  38.     public void setIsChecked(Boolean isChecked) {  
  39.         this.isChecked = isChecked;  
  40.     }  
  41. }  

别忘了在AndroidManifest.xml中加上权限:

[java] view plain copy
  1. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  

2、使用AsyncTask加载图片,由于从硬盘读取照片是耗时操作,我们不能直接在ui主线程里面去操作,这里用AsyncTask来进行图片读取。

[java] view plain copy
  1. class BitmapWorkerTask extends AsyncTask<String, Void, BitmapDrawable> {  
  2.     private String mPath;  
  3.     private final WeakReference<ImageView> imageViewReference;  
  4.   
  5.     public BitmapWorkerTask(String path, ImageView imageView) {  
  6.         mPath = path;  
  7.         imageViewReference = new WeakReference<ImageView>(imageView);  
  8.     }  
  9.   
  10.     // Decode image in background.  
  11.     @Override  
  12.     protected BitmapDrawable doInBackground(String... params) {  
  13.   
  14.         BitmapDrawable drawable = null;  
  15.         Bitmap bitmap = decodeBitmapFromDisk(mPath, mImageWidth, mImageHeight);  
  16.   
  17.         //Bitmap转换成BitmapDrawable  
  18.         if (bitmap != null) {  
  19.             drawable = new BitmapDrawable(mResources, bitmap);  
  20.         }  
  21.         return drawable;  
  22.     }  
  23.   
  24.     @Override  
  25.     protected void onPostExecute(BitmapDrawable value) {  
  26.         if (imageViewReference != null && value != null) {  
  27.             final ImageView imageView = imageViewReference.get();  
  28.             if (imageView != null) {  
  29.                 imageView.setImageDrawable(value);  
  30.             }  
  31.         }  
  32.     }  
  33. }  

从上面的代码中可以看出,我们在构造函数中把图片路径和要显示图片的ImageView引入进来,图片读取并压缩完成后,ImageView显示该图片。这里我们并没有直接强引用ImageView,而是使用了弱引用(WeakReference),原因在于读取图片是耗时操作,有可能在图片未读取完成时,我们的ImageView已经被划出屏幕,这时候如果AsyncTask仍持有ImageView的强引用,那会阻止垃圾回收机制回收该ImageView,使用弱引用就不会阻止垃圾回收机制回收该ImageView,可以有效避免OOM。

 定义好task后,我们可以这么来使用:

[java] view plain copy
  1. public void loadImage(String path, ImageView imageView) {  
  2.     BitmapWorkerTask task = new BitmapWorkerTask(path,imageView);  
  3.     task.execute();  
  4. }  

上面定义的AsyncTask中,decodeBitmapFromDisk(mPathmImageWidth,mImageHeight)是压缩图片并读取出来的方法。

[java] view plain copy
  1. /** 
  2.  * 根据路径从硬盘中读取图片 
  3.  * @param path 图片路径 
  4.  * @param reqWidth 请求宽度(显示宽度) 
  5.  * @param reqHeight 请求高度(显示高度) 
  6.  * @return 图片Bitmap 
  7.  */  
  8. public Bitmap decodeBitmapFromDisk(String path, int reqWidth, int reqHeight) {  
  9.     BitmapFactory.Options options = new BitmapFactory.Options();  
  10.     options.inJustDecodeBounds = true;  
  11.     BitmapFactory.decodeFile(path, options);  
  12.     //初始压缩比例  
  13.     options.inSampleSize = calculateBitmapSize(options, reqWidth, reqHeight);  
  14.     options.inJustDecodeBounds = false;  
  15.     Bitmap bmp = BitmapFactory.decodeFile(path, options);  
  16.     return bmp;  
  17. }  
  18.   
  19. /** 
  20.  * 计算压缩率 
  21.  * @param options 
  22.  * @param reqWidth 
  23.  * @param reqHeight 
  24.  * @return 
  25.  */  
  26. public static int calculateBitmapSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {  
  27.     // Raw height and width of image  
  28.     final int height = options.outHeight;  
  29.     final int width = options.outWidth;  
  30.     int inSampleSize = 1;  
  31.   
  32.     if (height > reqHeight || width > reqWidth) {  
  33.   
  34.         final int halfHeight = height / 2;  
  35.         final int halfWidth = width / 2;  
  36.   
  37.         // Calculate the largest inSampleSize value that is a power of 2 and keeps both  
  38.         // height and width larger than the requested height and width.  
  39.         while ((halfHeight / inSampleSize) > reqHeight  
  40.                 && (halfWidth / inSampleSize) > reqWidth) {  
  41.             inSampleSize *= 2;  
  42.         }  
  43.     }  
  44.     return inSampleSize;  
  45. }  

calculateBitmapSize这个方法的主要功能就是根据要显示的图片大小,计算压缩率,压缩原始图片并读出压缩后的图片,在上一篇博客里面有详细介绍。

 接下来就是在gridview中,定义adapter,把图片显示出来,具体代码这里不贴出来,不是这篇博客的主要内容,后面我会把源码上传,博客中没有贴出来的代码,可以在源码中查看。

 下面看我们做到这一步之后的效果。



可以看到效果很不流畅,滑动屏幕时,ImageView显示的图片一直在变换,原因一开始就讲过了,这是并发导致的。复用ImageView导致同个ImageView对应了多个AsyncTask,每个AsyncTask完成时都会改变ImageView显示的图片。而且AsyncTask完成顺序是不确定的,所以也会导致图片错位,本来应该显示1位置的图片的ImageView结果显示的21位置的图片。

3、处理并发性

       要解决上面的问题,我们就应该让一个ImgeView只对应一个AsyncTask,当有新的AsyncTask进入时,先看ImgeView上是否有AsyncTask正在执行,如果有,则取消该AsyncTask,然后把新的AsyncTask加入进来,这样不止解决了图片错位问题,同时也减少了没必要的AsyncTask,提高了加载效率。

定义一个持有AsyncTask弱引用的BitmapDrawable类

[java] view plain copy
  1. static class AsyncDrawable extends BitmapDrawable {  
  2.     private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;  
  3.   
  4.     public AsyncDrawable(Resources res, Bitmap bitmap,  
  5.                          BitmapWorkerTask bitmapWorkerTask) {  
  6.         super(res, bitmap);  
  7.         bitmapWorkerTaskReference =  
  8.                 new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);  
  9.     }  
  10.   
  11.     public BitmapWorkerTask getBitmapWorkerTask() {  
  12.         return bitmapWorkerTaskReference.get();  
  13.     }  
  14. }  

然后用imageView.setImageDrawable(asyncDrawable)把ImageView和AsyncDrawable绑定,这样就可以把ImageView与AsyncTask对应起来。

         为什么使用弱引用我们上面讲了,为什么AsyncTask要持有ImageView的引用我们上面也讲了,那么这里为什么要ImageView持有AsyncTask的引用呢?

         ImageView持有AsyncTask的引用,就可以通过ImageView找到其当前对应的AsyncTask,如果有新的AsyncTask进来,先比较是否和当前的AsyncTask一样,如果一样,则不把新的AsyncTask加入,如果不一样,先把当前对应的AsyncTask取消,再把新的AsyncTask与ImageView对应起来。

          这里还有一个问题,为什么不和前面AsyncTask持有ImageView弱引用一样,也在ImageView构造函数中让ImageView持有AsyncTask的弱引用就行,不用拐弯抹角的让ImageDrable持有AsyncTask的弱引用。这里要注意一下,我们的ImageView是复用的,也就是一般情况下,ImageView只构造了一次,如果ImageView直接持有AsyncTask的弱引用,那么只会持有ImageView刚构造时的那一个,而不会随着界面的滑动而更新AsyncTask。但是界面滑动时,ImageView的setImageDrawable方法却随着被触发,所以这里在ImageDrawable中持有AsyncTask的弱引用,然后ImageView通过getImageDrawable获得ImageDrawable,再通过ImageDrawable获得AsyncTask。

 修改loadImage方法

[java] view plain copy
  1. public void loadImage(String path, ImageView imageView) {  
  2.     if (path == null || path.equals("")) {  
  3.         return;  
  4.     }  
  5.     BitmapDrawable bitmapDrawable = null;  
  6.      
  7.     if (bitmapDrawable != null) {  
  8.         imageView.setImageDrawable(bitmapDrawable);  
  9.     } else if (cancelPotentialWork(path,imageView)) {  
  10.         final BitmapWorkerTask task = new BitmapWorkerTask(path,imageView);  
  11.         final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mLoadingBitmap, task);  
  12.         imageView.setImageDrawable(asyncDrawable);  
  13.         task.execute();  
  14.     }  
  15. }  
  16.   
  17. public static boolean cancelPotentialWork(String path, ImageView imageView) {  
  18.     final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  19.   
  20.     if (bitmapWorkerTask != null) {  
  21.         final String bitmapData = bitmapWorkerTask.mPath;  
  22.         // If bitmapData is not yet set or it differs from the new data  
  23.         if (bitmapData == null|| !bitmapData.equals(path)) {  
  24.             // Cancel previous task  
  25.             bitmapWorkerTask.cancel(true);  
  26.         } else {  
  27.             // The same work is already in progress  
  28.             return false;  
  29.         }  
  30.     }  
  31.     // No task associated with the ImageView, or an existing task was cancelled  
  32.     return true;  
  33. }  
  34.   
  35. private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {  
  36.     if (imageView != null) {  
  37.         final Drawable drawable = imageView.getDrawable();  
  38.         if (drawable instanceof AsyncDrawable) {  
  39.             final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;  
  40.             return asyncDrawable.getBitmapWorkerTask();  
  41.         }  
  42.     }  
  43.     return null;  
  44. }  

修改BitmapWorkerTask 方法

[java] view plain copy
  1. class BitmapWorkerTask extends AsyncTask<String, Void, BitmapDrawable> {  
  2.     private String mPath;  
  3.     private final WeakReference<ImageView> imageViewReference;  
  4.   
  5.     public BitmapWorkerTask(String path, ImageView imageView) {  
  6.         mPath = path;  
  7.         imageViewReference = new WeakReference<ImageView>(imageView);  
  8.     }  
  9.   
  10.     // Decode image in background.  
  11.     @Override  
  12.     protected BitmapDrawable doInBackground(String... params) {  
  13.   
  14.         BitmapDrawable drawable = null;  
  15.         Bitmap bitmap = decodeBitmapFromDisk(mPath, mImageWidth, mImageHeight);  
  16.   
  17.         //Bitmap转换成BitmapDrawable  
  18.         if (bitmap != null) {  
  19.             drawable = new BitmapDrawable(mResources, bitmap);  
  20.         }  
  21.         return drawable;  
  22.     }  
  23.   
  24.     @Override  
  25.     protected void onPostExecute(BitmapDrawable value) {  
  26.         if (isCancelled()) {  
  27.             value = null;  
  28.         }  
  29.   
  30.         if (imageViewReference != null && value != null) {  
  31.             final ImageView imageView = imageViewReference.get();  
  32.             final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  33.             if (this == bitmapWorkerTask && imageView != null) {  
  34.                 imageView.setImageDrawable(value);  
  35.             }  
  36.         }  
  37.     }  
  38. }  

再看效果



已经运行比较流畅了。

4、图片缓存

         图片缓存,老的做法是使用SoftReference 或者WeakReference bitmap缓存,但是不推荐使用这种方式。因为从Android 2.3 (API Level 9) 开始,垃圾回收开始强制的回收掉soft/weak 引用从而导致这些缓存没有任何效率的提升。另外,在 Android 3.0 (API Level 11)之前,这些缓存的Bitmap数据保存在底层内存(nativememory)中,并且达到预定条件后也不会释放这些对象,从而可能导致程序超过内存限制并崩溃(OOM)。

         现在常用的做法是使用LRU算法来缓存图片,把最近使用到的Bitmap对象用强引用保存起来(保存到LinkedHashMap中),当缓存数量达到预定的值的时候,把不经常使用的对象删除。Android提供了LruCache类(在API 4之前可以使用SupportLibrary 中的类),里面封装了LRU算法,因此我们不需要自己实现,只要分配好内存空间就可以。

定义ImageCache类用于图片缓存。

[java] view plain copy
  1. public class ImageCache {  
  2.   
  3.     private LruCache<String, BitmapDrawable> mMemoryCache;  
  4.   
  5.     public ImageCache() {  
  6.   
  7.         // 获取应用最大内存  
  8.         final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  9.         //用最大内存的1/4来缓存图片  
  10.         final int cacheSize = maxMemory / 4;  
  11.         mMemoryCache = new LruCache<String, BitmapDrawable>(cacheSize) {  
  12.             /** 
  13.              * Measure item size in kilobytes rather than units which is more practical 
  14.              * for a bitmap cache 
  15.              */  
  16.             @Override  
  17.             protected int sizeOf(String key, BitmapDrawable value) {  
  18.                 Bitmap bitmap = value.getBitmap();  
  19.                 return bitmap.getByteCount() / 1024;  
  20.             }  
  21.         };  
  22.     }  
  23.   
  24.     /** 
  25.      * Adds a bitmap to both memory and disk cache. 
  26.      * @param data Unique identifier for the bitmap to store 
  27.      * @param value The bitmap drawable to store 
  28.      */  
  29.     public void addBitmapToMemCache(String data, BitmapDrawable value) {  
  30.   
  31.         if (data == null || value == null) {  
  32.             return;  
  33.         }  
  34.   
  35.         // Add to memory cache  
  36.         if (mMemoryCache != null) {  
  37.             mMemoryCache.put(data, value);  
  38.         }  
  39.   
  40.     }  
  41.   
  42.     /** 
  43.      * Get from memory cache. 
  44.      * 
  45.      * @param data Unique identifier for which item to get 
  46.      * @return The bitmap drawable if found in cache, null otherwise 
  47.      */  
  48.     public BitmapDrawable getBitmapFromMemCache(String data) {  
  49.   
  50.         BitmapDrawable memValue = null;  
  51.   
  52.         if (mMemoryCache != null) {  
  53.             memValue = mMemoryCache.get(data);  
  54.         }  
  55.         return memValue;  
  56.     }  
  57.   
  58. }  

接下来就是修改获取图片的方法:先从内存缓存查找图片,找不到再开启task去硬盘读取。

[java] view plain copy
  1. public void loadImage(String path, ImageView imageView) {  
  2.     if (path == null || path.equals("")) {  
  3.         return;  
  4.     }  
  5.     BitmapDrawable bitmapDrawable = null;  
  6.       
  7.     //先从缓存读取  
  8.     if(mImageCache != null){  
  9.         bitmapDrawable = mImageCache.getBitmapFromMemCache(path);  
  10.     }  
  11.     //读取不到再开启任务去硬盘读取  
  12.     if (bitmapDrawable != null) {  
  13.         imageView.setImageDrawable(bitmapDrawable);  
  14.     } else if (cancelPotentialWork(path,imageView)) {  
  15.         final BitmapWorkerTask task = new BitmapWorkerTask(path,imageView);  
  16.         final AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mLoadingBitmap, task);  
  17.         imageView.setImageDrawable(asyncDrawable);  
  18.         task.execute();  
  19.     }  
  20. }  

task中读取到图片之后,同时把该图片加入到缓存中

[java] view plain copy
  1. class BitmapWorkerTask extends AsyncTask<String, Void, BitmapDrawable> {  
  2.     private String mPath;  
  3.     private final WeakReference<ImageView> imageViewReference;  
  4.   
  5.     public BitmapWorkerTask(String path, ImageView imageView) {  
  6.         mPath = path;  
  7.         imageViewReference = new WeakReference<ImageView>(imageView);  
  8.     }  
  9.   
  10.     // Decode image in background.  
  11.     @Override  
  12.     protected BitmapDrawable doInBackground(String... params) {  
  13.   
  14.         BitmapDrawable drawable = null;  
  15.         Bitmap bitmap = decodeBitmapFromDisk(mPath, mImageWidth, mImageHeight);  
  16.   
  17.         //Bitmap转换成BitmapDrawable  
  18.         if (bitmap != null) {  
  19.             drawable = new BitmapDrawable(mResources, bitmap);  
  20.             //缓存  
  21.             if(mImageCache!=null){  
  22.                 mImageCache.addBitmapToMemCache(mPath, drawable);  
  23.             }  
  24.         }  
  25.         return drawable;  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void onPostExecute(BitmapDrawable value) {  
  30.         if (isCancelled()) {  
  31.             value = null;  
  32.         }  
  33.   
  34.         if (imageViewReference != null && value != null) {  
  35.             final ImageView imageView = imageViewReference.get();  
  36.             final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  37.             if (this == bitmapWorkerTask && imageView != null) {  
  38.                 imageView.setImageDrawable(value);  
  39.             }  
  40.         }  
  41.     }  
  42. }  

再看效果

会发现第一次加载完图片之后,再往回滑动查看时,图片很快就显示出来。做到这一步其实就已经可以了,不过还可以继续优化。

 Android 3.0 (API level 11)之后, BitmapFactory.Options提供了一个属性 inBitmap,该属性使得Bitmap解码器去尝试重用已有的bitmap,这样就可以减少内存的分配和释放,提高效率。

 需要注意的是,在Android4.4之前,重用的bitmap大小必须一样,4.4之后,新申请的Bitmap大小必须小于或者等于已经赋值过的Bitmap大小,所以实际上这个属性4.4之后的作用才比较明显

 bitmapLruCache被移出时,将移出的bitmap以软引用的形式放进HashSet,用于后面的重用。

[java] view plain copy
  1. private LruCache<String, BitmapDrawable> mMemoryCache;  
  2. private Set<SoftReference<Bitmap>> mReusableBitmaps;  
  3. if (Utils.hasHoneycomb()) {  
  4.     mReusableBitmaps = Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());  
  5. }  
  6.   
  7. // 获取应用最大内存  
  8. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  9. //用最大内存的1/4来缓存图片  
  10. final int cacheSize = maxMemory / 4;  
  11. mMemoryCache = new LruCache<String, BitmapDrawable>(cacheSize) {  
  12.     /** 
  13.      * Measure item size in kilobytes rather than units which is more practical 
  14.      * for a bitmap cache 
  15.      */  
  16.     @Override  
  17.     protected int sizeOf(String key, BitmapDrawable value) {  
  18.         Bitmap bitmap = value.getBitmap();  
  19.         return bitmap.getByteCount() / 1024;  
  20.     }  
  21.   
  22.     /** 
  23.      * Notify the removed entry that is no longer being cached 
  24.      */  
  25.     @Override  
  26.     protected void entryRemoved(boolean evicted, String key,  
  27.                                 BitmapDrawable oldValue, BitmapDrawable newValue) {  
  28.   
  29.         // The removed entry is a standard BitmapDrawable  
  30.         if (Utils.hasHoneycomb()) {  
  31.             // We're running on Honeycomb or later, so add the bitmap  
  32.             // to a SoftReference set for possible use with inBitmap later  
  33.             mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));  
  34.         }  
  35.     }  
  36. };  

在解码的时候,尝试使用 inBitmap。

[java] view plain copy
  1. public Bitmap decodeBitmapFromDisk(String path, int reqWidth, int reqHeight) {  
  2.     // BEGIN_INCLUDE (read_bitmap_dimensions)  
  3.     // First decode with inJustDecodeBounds=true to check dimensions  
  4.     final BitmapFactory.Options options = new BitmapFactory.Options();  
  5.     options.inJustDecodeBounds = true;  
  6.     BitmapFactory.decodeFile(path, options);  
  7.   
  8.     // Calculate inSampleSize  
  9.     options.inSampleSize = calculateBitmapSize(options, reqWidth, reqHeight);  
  10.     // END_INCLUDE (read_bitmap_dimensions)  
  11.   
  12.     // If we're running on Honeycomb or newer, try to use inBitmap  
  13.     if (Utils.hasHoneycomb()) {  
  14.         addInBitmapOptions(options);  
  15.     }  
  16.   
  17.     // Decode bitmap with inSampleSize set  
  18.     options.inJustDecodeBounds = false;  
  19.     return BitmapFactory.decodeFile(path, options);  
  20. }  
  21.   
  22. private  void addInBitmapOptions(BitmapFactory.Options options) {  
  23.   
  24.     // inBitmap only works with mutable bitmaps so force the decoder to  
  25.     // return mutable bitmaps.  
  26.     options.inMutable = true;  
  27.   
  28.     if (mImageCache != null) {  
  29.         // Try and find a bitmap to use for inBitmap  
  30.         Bitmap inBitmap = mImageCache.getBitmapFromReusableSet(options);  
  31.   
  32.         if (inBitmap != null) {  
  33.             options.inBitmap = inBitmap;  
  34.         }  
  35.     }  
  36. }  

下面的方法从ReusableSet查找是否有可以重用的inBitmap

[java] view plain copy
  1. /** 
  2.  * @param options - BitmapFactory.Options with out* options populated 
  3.  * @return Bitmap that case be used for inBitmap 
  4.  */  
  5. protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {  
  6.     Bitmap bitmap = null;  
  7.     if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {  
  8.         synchronized (mReusableBitmaps) {  
  9.             final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps.iterator();  
  10.             Bitmap item;  
  11.             while (iterator.hasNext()) {  
  12.                 item = iterator.next().get();  
  13.                 if (null != item && item.isMutable()) {  
  14.                     // Check to see it the item can be used for inBitmap  
  15.                     if (canUseForInBitmap(item, options)) {  
  16.                         bitmap = item;  
  17.                         // Remove from reusable set so it can't be used again  
  18.                         iterator.remove();  
  19.                         break;  
  20.                     }  
  21.                 } else {  
  22.                     // Remove from the set if the reference has been cleared.  
  23.                     iterator.remove();  
  24.                 }  
  25.             }  
  26.         }  
  27.     }  
  28.   
  29.     return bitmap;  
  30.   
  31. }  

最后,下面方法确定候选位图尺寸是否满足inBitmap

[java] view plain copy
  1. /** 
  2.  * @param candidate - Bitmap to check 
  3.  * @param targetOptions - Options that have the out* value populated 
  4.  * @return true if <code>candidate</code> can be used for inBitmap re-use with 
  5.  *      <code>targetOptions</code> 
  6.  */  
  7. @TargetApi(Build.VERSION_CODES.KITKAT)  
  8. private static boolean canUseForInBitmap(  
  9.         Bitmap candidate, BitmapFactory.Options targetOptions) {  
  10.   
  11.     if (!Utils.hasKitKat()) {  
  12.         // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1  
  13.         return candidate.getWidth() == targetOptions.outWidth  
  14.                 && candidate.getHeight() == targetOptions.outHeight  
  15.                 && targetOptions.inSampleSize == 1;  
  16.     }  
  17.   
  18.     // From Android 4.4 (KitKat) onward we can re-use if the byte size of the new bitmap  
  19.     // is smaller than the reusable bitmap candidate allocation byte count.  
  20.     int width = targetOptions.outWidth / targetOptions.inSampleSize;  
  21.     int height = targetOptions.outHeight / targetOptions.inSampleSize;  
  22.     int byteCount = width * height * getBytesPerPixel(candidate.getConfig());  
  23.     return byteCount <= candidate.getAllocationByteCount();  
  24.   
  25. }  
  26.   
  27. /** 
  28.  * Return the byte usage per pixel of a bitmap based on its configuration. 
  29.  * @param config The bitmap configuration. 
  30.  * @return The byte usage per pixel. 
  31.  */  
  32. private static int getBytesPerPixel(Bitmap.Config config) {  
  33.     if (config == Bitmap.Config.ARGB_8888) {  
  34.         return 4;  
  35.     } else if (config == Bitmap.Config.RGB_565) {  
  36.         return 2;  
  37.     } else if (config == Bitmap.Config.ARGB_4444) {  
  38.         return 2;  
  39.     } else if (config == Bitmap.Config.ALPHA_8) {  
  40.         return 1;  
  41.     }  
  42.     return 1;  
  43. }  

自此就把本地相册图片加载到我们自己定义的gridview中,这个gridview要怎么设计,单选,多选,混合选,就随各位喜欢了.

源码下载地址:  http://download.csdn.net/detail/weihuangcool/9127635

另外作者推荐:ImagePicker同样实现了单多张图片选择,地址 : https://github.com/jeasonlzy/ImagePicker

0 0