图片有各种大小和形状,很多时候我们要显示的图片的分辨率大小远大于手机屏幕的分辨率,这时候我们通常是先对图片做压缩再加载到内存显示,因为一方面原图占用太多内存,容易导致OOM,另一方面,只要压缩适当,压缩后的图片在手机上的显示效果和原图没太大差别,手机分辨率就这么多,你原图分辨率再高,显示在手机屏幕上,也超不过其最大分辨率,视觉上看起来效果一样,占用的内存却大大不一样。
BitmapFactory提供了对图片操作的一系列方法,下面是对图片压缩的一个写法。
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);
}
开始时将inJustDecodeBounds属性设置为true,避免为图片分配内存,但是却可以读取图片的大小,再通过这个大小计算出压缩倍数inSampleSize,之后再将inJustDecodeBounds属性设置为false,读取压缩后的图片,并为其分配内存。下面看一下计算inSampleSize的具体实现。
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;
}
这里有两个问题:
1、 为什么要先把高宽除以2.
2、 为什么每次都是inSampleSize*2,也就是为什么inSampleSize的值是2的幂。
高宽除以2,是因为存在这么一种情况,原图高宽除以2之后,大小比要显示的高宽要小,这个时候如果inSampleSize*2的话,图片会有些失真。例如原图是100*100,而要显示的图的大小是80*80,如果设置inSampleSize为2,那么压缩后的图为50*50,50*50的图片显示在80*80的ImageView中,会有些失真,所以这个时候inSampleSize应该为1,将100*100的图片显示在80*80的ImageView中,就不会失真。
inSampleSize的值是2的幂,是因为解码器使用基于2的幂值, inSampleSize的其他任何值将被四舍五入到最近的2的幂(Note: thedecoder uses a final value based on powers of 2, any other value will berounded down to the nearest power of 2.)。所以我们这里直接就把inSampleSize处理为2的幂值。
当然,计算inSampleSize的方法可以根据实际情况来设置,例如你就是要把图片压缩的占用内存更小,不考虑失真,那么完全可以把inSampleSize设置为一个更大的值。
根据上面的方法,假设想在100*100的ImageView中显示图片,就可以这么写了:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));