本文译自:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
图片有各种形状和尺寸。在很多情况下,它们通常要大于显示图片用的用户界面(UI)。例如,系统的Gallery应用程序会显示Android设备的照相机所拍摄的照片,通常照片的分辨率要高于设备的屏幕密度。
由于内存的限制,你可能只想在内存中加载较低分辨率的图片版本。低分辨率版本应该跟显示它的UI组件尺寸相匹配。高分辨率的图片不会有益于显示,但是依然会消耗高贵的内存,并且由于缩放还会导致性能下降。
本文讲解如何用较小的采样版本来解码大位图,而不让其超过每个应用程序内存的限制。
读取位图的尺寸和类型
BitmapFactory类提供了几个解码方法(如decodeByteArray()、decodeFile()、decodeResource()等)来创建来自各种资源的位图(Bitmap)。基于图片数据源来选择最恰当的解码方法。这些方法尝试着给被创建的位图分配内存,内存很容易产生OutOfMemory异常。每种类型的解码方法都有一个附加的标识,这个标识会通过BitmapFactory.Options类让你指定解码选项。
把inJustDecodeBounds属性设置为true,解码时会避免过度的内存分配,除了设置outWidth、outHeight和outMimeType属性设置以外的位图对象都会返回null。这种技术会允许你在构造(和分配内存)位图之前读取图片数据打的尺寸和类型。
BitmapFactory.Options options =newBitmapFactory.Options();
options.inJustDecodeBounds= true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
要避免java.lang.OutOfMemory异常,就要在解码之前检查位图的尺寸,除非你确信提供给你的图片数据源能够适合可用的内存。
把缩小的图片加载到内存中
获取图片的尺寸之后,就可以用它来判断是否可以把图片完整的加载到内存中,或者是降低采样率来加载图片。以下是要考虑的一些因素:
1. 估算把图片完整的加载到内存时所有使用的内存;
2. 除了图片的内存需求之外,应用程序的其他内存需求;
3. 显示图片的UI组件的尺寸;
4. 当前设备的屏幕尺寸和密度。
例如,把一个1024x768像素的图片完全加载到内存中,并在一个128x96像素的ImageView组件中,这种内存消耗是不值得的。
设置BitmapFactory.Options对象的inSampleSize属性,就可以告诉解码器降低采样率,把较小的分辨率的图片加载到内存中。例如,inSampleSize属性设置为4,那么分辨率为2048x1536的图片就可以解码为512x384像素的位图。这时所消耗的内存是0.75MB而不是12MB(假设位图是ARGB_8888形态)。以下是基于目标的宽度和高度来计算采样尺寸值的方法:
publicstaticintcalculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width ofimage
final int height = options.outHeight;
final int width = options.outWidth;
intinSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculateratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choosethe smallest ratio as inSampleSize value, this will guarantee
// a finalimage with both dimensions larger than or equal to the
// requestedheight and width.
inSampleSize = heightRatio < widthRatio ?heightRatio : widthRatio;
}
return inSampleSize;
}
注意:inSampleSize值使用2的倍数会使解码器更快、更高效。但是,如果你只是打算在内存或硬盘上缓存调整后版本,那么通常采用最恰当的图片尺寸来解码,以便节省存储空间,这依然是值得的。
要使用这种方法,首先要把inJustDecodeBounds设置为true进行一次解码,然后再使用新的inSampleSize的值来再次解码,并把inJustDecodeBounds设置为false:
publicstaticBitmapdecodeSampledBitmapFromResource(Resources res,int resId,
int reqWidth, int reqHeight) {
// First decode withinJustDecodeBounds=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 withinSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
这种方法使得把任意大尺寸的图片加载到一个显示100x100像素的小ImageView中来显示变得很容易。例如:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
你可以使用类似过程对来自其他类型资源的位图进行解码,只需用相应的BitmapFactory.decode*方法来代替就可以了。