图像拥有各种形状和大小。在许多情况下,他们大于所需的一个典型的应用程序的用户界面(UI)。例如,系统Gallery应用程序显示使用您的Android设备的摄像头拍摄的照片,而这些照片的分辨率将大于你的屏幕所拥有的。
鉴于您正在使用的内存有限,理想情况下,你只需要在内存中加载一个低分辨率。较低分辨率的版本应该与显示它的UI组件的大小相匹配。具有较高的图像分辨率不提供任何可见的好处,但仍占用宝贵的内存和产生额外的性能开销由于额外的缩放。这节课引导您在不超过每个应用程序的内存限制的情况下,通过解码大位图,在内存加载较小的子采样版本。
读取位图尺寸和类型
BitmapFactory类提供几种解码方法(decodeByteArray(),decodeFile(),decodeResource(),等等),用于从各种来源创建一个位图。基于图像数据源选择最合适的解码方法。这些方法试图为构建的位图分配内存,因此很容易导致OutOfMemory例外。
每种类型的解码方法有附加的签名,让你通过BitmapFactory.Options类指定解码选项。设置inJustDecodeBounds属性设置为true ,解码时避免内存分配,设置outWidth,outHeight outMimeType并返回null位图对象。这种技术允许你在位图内存分配前读取的图像数据的尺寸和类型。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
加载到内存的缩小版本
现在的图像的尺寸是已知的,它们可以被用来决定二次采样的版本或者完整的图像是否应该被加载到内存中。这里有一些因素需要考虑:
1.估计加载全部图像使用的内存。
2.加载此图像你愿意给予的内存
3.目标的ImageView或UI组件被加载的尺寸。
4.当前的移动设备的屏幕尺寸和密度。
例如,不值得1024x768像素图像加载到内存中,如果它最终会被显示在ImageView的一个128X96像素的缩略图。
要告诉解码器加载一个较小的版本到内存中,设置BitmapFactory.Options对象的inSampleSize为true。例如, 2048×1536分辨率的图像进行解码inSampleSize 4产生约512X384的位图。加载到内存使用0.75MB而不是12MB(假设ARGB_8888位图配置)。这里有一个基于一个目标宽度和高度值来计算的样本大小的方法:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios 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);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
注:inSampleSize值越大会影响效率,但节省空间。
下面代码使用了这个方法:
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);
}
以下代码将载入的大位图显示在一个100*100的imageview中
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));