由于你工作在有限的内存,最理想的情况是在内存中加载低分辨率的图片。低分辨率的图片只要匹配展示图片的UI组件的大小就行了。高分辨率的图片并不会给视觉带来更多益处,但是却占用了宝贵的内存,而且在滑动缩放的时候易引发性能问题。
本次课程教你在内存中通过加载小的样本位图来解码大的位图,而不会超过每个应用程序的内存限制。
【读取位图尺寸和类型】
BitmapFactory类提供了一些解码方法 (decodeByteArray(), decodeFile(), decodeResource(), 等.) 从各种源来创建位图Bitmap。选择最合适的解码方法依赖于你的图片数据源。这些方法试图按照bitmap的构造方法分配内存,因此很容易导致OutOfMemory异常。每一种解码方法还有额外的通过 BitmapFactory.Options来指定解码选项的签名,在解码时设置inJustDecodeBounds属性为真可以避免分配内存,虽然返回的是空位图,但是图片的outWidth, outHeight 和outMimeType属性已经获得。这种技术可以让在创建位图(和内存分配)前获取位图的尺寸和类型。
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;
为了避免 java.lang.OutOfMemory 内存溢出异常,在解码图片前要检查图片的尺寸,除非确信为您提供的是可预见尺寸的,占用内存合适的图像数据源。
【在内存中加载一个缩小版的图片】
现在图片的尺寸已经知道,可以用来在内存中加载全尺寸的图片或者加载缩小的子样本图片。这里有一些因素需要考虑:
*估计加载全尺寸内存使用情况
*对于加载此图片,你的应用程序能够给它分配的内存数量
*此图片装载的ImageView或者UI组件的尺寸
*当前设备的屏幕大小和密度
例如,把1024×768像素的图像载入到内存,如果它最终将在ImageView里显示一个128X96像素的缩略图,这是不值得的。
告诉解码器加载一个缩小版的图片样本到内存,在 BitmapFactory.Options对象里设置 inSampleSize为真,例如一个分辨率为2048*1536的图片用inSampleSize为4解码,将会生成一个约512*384的图片。加载此图片到内存只会占用0.75MB而不是全尺寸的12MB(假设位图配置为ARGB_8888)。下面提供一个方法来计算一个图片样本的大小值,该值是2的幂次方,基于目标宽度和高度计算。
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) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
注意:2的幂次方值的计算,是解码器通过向下舍入到最近的2的幂的值,参见inSampleSize文档。
为了使用此方法,第一步将 inJustDecodeBounds设置为true,传递options参数,然后使用新的 inSampleSize值,并设置inJustDecodeBounds为false:
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));
你可以通过相似的方法处理从其他源来解码的位图,通过替换合适的BitmapFactory.decode* 解码方法。
翻译自:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html