Bitmap是Android系统中的图像处理的最重要的类之一。通常对于一般小的图片的加载时没有什么问题的,但是如果如果加载比较的大的图片,就很容易发生OOM问题,就是所谓的内存溢出,所以我们就要对图片进行加载管理,就是通过缩放来加载图片。
首先,Bitmap的加载是通过BitmapFactory类提供的一些方法,decodeFile(),decodeResoure(),decodeStream(),decodeByteArray(),decodeFileDescriptor()
其中decodeFile()和decodeResource()最终是调用了decodeStream(),这从源码可以看出:
public static Bitmap decodeFile(String pathName, Options opts) {
Bitmap bm = null;
InputStream stream = null;
try {
stream = new FileInputStream(pathName);
bm = decodeStream(stream, null, opts);
}
........
return bm;
}
public static Bitmap decodeResource(Resources res, int id, Options opts) {
.....
bm = decodeResourceStream(res, value, is, null, opts);
.....
return bm;
}
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
........
return decodeStream(is, pad, opts);
}
如何加载Bitmap,就是通过缩放。通常,ImageView要显示的图片大小没有那么大,但是如果一开始就整张图片加载进来再进行设置,这就很浪费资源了,我们就可以根据ImageView的宽高需求进行加载。这时候就要通过BitmapFactory.Options来加载大小,这里面还涉及一个采样率inSampleSize。
当inSampleSize为1的时候,图片不进行缩放;当inSampleSize为2的时候,加载的图片均为原来的1/2,整张图片就是原来的1/4大小了,假如原始图片是4M,那么加载进来就只有1M,大大节省空间。
通过采样率加载图片通常有以下四步:
- 设置BitmapFactory.Options的inJustDecodeBounds值为true,此时BitmapFactory只会解析图片的原始高度
- 根据BitmapFactory.Options获取图片的原始高度和宽度信息
- 根据图片的原始高度计算出采样率options.inSampleSize
- 设置BitmapFactory.Options的inJustDecodeBounds值为false,此时BitmapFactory会根据采样率加载图片
public Bitmap decodeSampleFromResource(Resources res,int resId,int reqWidth,int reqHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
//此时,BitmapFactory只会解析图片的原始高度和宽度
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,options);
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
//此时就会根据inSample来加载
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res,resId,options);
}
public int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
//原图片的真实高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
//当图片高度或者宽度大的话,就要进行压缩
if (height>reqHeight || width>reqWidth){
final int halfWidth = width / 2;
final int halfHeight = height / 2;
while ((halfWidth / inSampleSize)>=reqWidth && (halfHeight / inSampleSize)>=reqHeight){
inSampleSize *= 2;
}
}
return inSampleSize;
}
注意
有些情况下,ImageView宽度和高度是100*100,而原始图片是200*200,当采样率为2的时候,图片就会刚好缩放到100*100。当原始图片是200*300的时候,采样率同为2,图片会缩放到100*150,也是可以的,当采样率为3的时候,就是66*100,那么图片就会被拉伸而变得模糊,为了防止这种情况发生,缩放的时候,必须缩放后的宽度和高度都要大于等于需求的高度,所以:
while ((halfWidth / inSampleSize)>=reqWidth && (halfHeight / inSampleSize)>=reqHeight)