高效的加载大尺寸图片

图片具有不同的形状和尺寸。通常情况下图片比设备的屏幕尺寸要大很多。例如,用Android系统的图库程序来显示手机摄像头拍摄的照片,这些照片的分辨率要比设备屏幕分辨率大很多。

考虑到程序可用的内存是有限的,所以您只要载入一个低分辨率的图片在内存中即可。这个低分辨率的图片应该和显示该图片的控件大小匹配。如果使用大的分辨率图片来显示在控件中并没有任何视觉提升,而且还要浪费很多内存和CPU性能。

 

这节内容演示了如何使用一个缩小后的图片来避免这些问题。

读取 Bitmap 的尺寸和类型

BitmapFactory
提供了一些解析图片的函数(decodeByteArray(), decodeFile(), decodeResource(), etc.)
来从不同的资源创建一个 Bitmap
根据您的图片所在的位置,选择不同的函数来创建图片。这些函数会申请需要的内存来创建图片,所以很容易导致 OutOfMemory 异常发生。
每个函数都有提供了解析图片的配置参数的重载函数,该参数为BitmapFactory.Options 类。
如果在解析图片的时候,设置该类的 inJustDecodeBounds 属性为 true
则不会申请内存并且返回 null 而不是一个Bitmap对象,同时还设置了
outWidth, outHeightoutMimeType 这些值。
这样您可以读取图片的尺寸和图片类型而不用载入该图片到内存中。

1
2
3
4
5
6
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true ;
BitmapFactory.decodeResource(getResources(), R.id.myp_w_picpath, options);
int p_w_picpathHeight = options.outHeight;
int p_w_picpathWidth = options.outWidth;
String p_w_picpathType = options.outMimeType;

要避免 java.lang.OutOfMemory 异常,在载入图片前先检查下图片的尺寸。如果你非常确信图片来源提供的图片都是尺寸合适的,您可以不用检查。

载入缩小过的图片到内存中

现在知道了图片的尺寸大小,您可以根据该信息来决定要载入整个图片到内存中还是只载入该图片的一个缩略图。下面是一些需要考虑的因素:

  • 估计载入整个图片所需要的内存
  • 根据 程序运行情况能分配多少内存供图片使用
  • 用来显示该图片的ImageView或者其他控件的尺寸
  • 当前设备的屏幕尺寸和屏幕密度

例如,如果最终只显示一个128X96像素的缩略图,是完全没有必要把整个 1024×768 像素的图片数据都载入到内存中的。

通过设置BitmapFactory.Options参数的属性 inSampleSize
true 来载入一个图片的缩略图。例如,对于一个 2048×1536 分辨率的图片,使用
inSampleSize 的参数为4,会生成一个
512×384的缩略图。载入该缩略图到内存中只需要0.75MB,而整个图片需要12MB的内存(假设使用 ARGB_8888 格式的图片)。
下面的代码根据目标尺寸来计算图片的采样率参数(inSampleSize)的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static int calculateInSampleSize(
             BitmapFactory.Options options, int reqWidth, int reqHeight) {
     // Raw height and width of p_w_picpath
     final int height = options.outHeight;
     final int width = options.outWidth;
     int inSampleSize = 1 ;
  
     if (height > reqHeight || width > reqWidth) {
         if (width > height) {
             inSampleSize = Math.round(( float )height / ( float )reqHeight);
         } else {
             inSampleSize = Math.round(( float )width / ( float )reqWidth);
         }
     }
     return inSampleSize;
}

注意: 对于参数 inSampleSize 而言,使用
2的平方值会提高解析效率。但是,如果您计划把缩略图缓存到内存或者磁盘中以便后续使用,通常还是建议使用尺寸匹配的缩略图来节省内存的消耗量。

要使用这种方式,先设置inJustDecodeBoundstrue
来得到图片的尺寸,然后在设置该值为 false同时设置计算好的 inSampleSize参数值来获取一个合适的缩略图。
:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
         int reqWidth, int reqHeight) {
  
     // First decode with inJustDecodeBounds=true to check dimensions
     final BitmapFactory.Options options = new BitmapFactory.Options();
     options.inJustDecodeBounds = true ;
     BitmapFactory.decodeResource(res, resId, options);
  
     // Calculate inSampleSize
     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
  
     // Decode bitmap with inSampleSize set
     options.inJustDecodeBounds = false ;
     return BitmapFactory.decodeResource(res, resId, options);
}

通过上面的函数,可以载入任意大小的图片到一个ImageView 来显示。如下代码
显示一个100X100的缩略图:

1
2
mImageView.setImageBitmap(
     decodeSampledBitmapFromResource(getResources(), R.id.myp_w_picpath, 100 , 100 ));



原文转载自 云在千峰: http://yunfeng.sinaapp.com/?p=393#ixzz20lN2Y2Xz