图片来源多种多样,多数情况下往往比要求的要大。例如,系统应用程序显示照片画廊通常使用Android设备的相机分辨率远高于你的设备的屏幕密度。
鉴于您在使用有限内存,理想情况下你需要加载一个低分辨率版本在内存中。低分辨率图片应该匹配显示它的UI组件大小。高分辨率图片不提供任何可见的好处,但是占据着宝贵的内存资源并且在动态缩放时带来额外的开销。
本节课教你解码大位图不超过每个应用程序内存限制通过加载一个更小的子样本版本图片在内存中。
Read Bitmap Dimensions and Type
BitmapFactory类提供多种解码方法decodeByteArray(), decodeFile(),decodeResource(), etc 从不同资源来创建一个位图。根据你的图片资源选择一个合适的解码方法。这些方法为构造的位图分配内存,因此会很容易发生OutOfMemory 异常.每种类型的解码方法有额外的签名,让你通过BitmapFactory指定解码选项。设置inJustDecodeBounds属性为true,在decoder时不会加载bitmap到内存,只会设置outWidth, outHeight and outMimeType的值.在构造该对象之前,通过该方法能读取到这个bitmap的图片尺寸和类型。
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;
为了避免内存溢出,在解码Bitmap之前检查其尺寸。
Load a Scaled Down Version into Memory
既然图片尺寸都知道了,可以决定是否需要原大图到内存还是加载一个子样本。以下是应该考虑的因素:.估计要加载的图片内存大小
.你愿意承诺的内存加载图片给其他应用程序的内存需求。
.加载图片的目标ImageView或者UI组件的尺寸
.当前设备的屏幕大小和密度
例如,它不值得加载一个1024*768的图片到内存中而最终被显示在128*96像素ImageView中。
在BitmapFactory.Options 中设置inSampleSize为ture来告诉子样品图像的解码器,加载一个低版本到内存中。例如,一个2048*1536的图片通过值为4的inSampleSize解码为512*384的位图。然后加载内存只会用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) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
标记:用2的方幂的inSampleSize 是更快、更有效的解码方式。然而,如果你计划缓存大小版本在内存或磁盘上,通常解码最合适的图片尺寸节省空间也是非常值得的。
用这个方法之前, inJustDecodeBounds设置为ture,用新的 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));
未完待续。。。