在实际项目中,会遇到很多需要处理各种图片的地方。在很多情况下,图片的分辨率会特别高。特别是现在手机发展速度快速,拍摄出来的照片动辄10多M,当我们查看这些照片的时候,如果不经过任何处理,不仅在加载速度会很感人,而且耗费的内存很可能导致内存超出应用的可用内存,所以在加载大分辨率图片之前进行适当的处理十分有必要。
处理的步骤主要分为如下两部分:
读取带加载图片的尺寸和类型
我们知道,BitmapFactory
提供了几种解码方式来将图片转化为Bitmap
格式,比如(decodeByteArray(), decodeFile(), decodeResource()
等等),这些方式分别对应来源和存放位置不同的图片资源。我们应当选择合适的方法来加载相应的图片。但是这些方法会为了构造Bitmap
而直接分配内存,这样很容易因为图片资源分辨率过大而造成OOM。Google官方也考虑这些方法的缺陷,做出了相应的弥补。构建一个BitmapFactory.options
实例,然后将它其中的一个参数inJustDecodeBounce
置为true
,传入到上述方法中:
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;
这样的话,decodeResource
方法就暂时不会创建Bitmap
对象,也不会消耗内存。而我们可以通过options
拿到图片你的长宽以及类型,这些属性对后面处理图片有很重要的作用。
根据图片的尺寸与控件尺寸对图片进行缩放
现在我们知道了源图片的尺寸参数,就可以根据尺寸参数来决定是直接加载原图还是预处理缩小后在加载图片。做出选择主要需要考虑一下的因素:
- 预估直接加载图片的内存消耗
- 考虑你可以接受的内存消耗
- 加载图片的ImageView或者其他控件的实际尺寸
- 屏幕大小和分辨率
举个例子,没有必要向一个尺寸只有128*96的ImageView
传入一个分辨率为1024*768的图片。
如果确定需要缩小当前的图片资源,那么我们得搞清楚到底该缩小多少。在options
中有一个参数inSampleSize
,他是确定最后实际生成的Bitmap
和原图之间分辨率的一个比例。比如,原图为2048*1536而inSampleSize
为4的话,最后通过上述解码方法生成的Bitmap
的实际分辨率就是512*384. 同时内存消耗也从12MB减少到了0.75MB(假设Bitmap的设置是ARGB_8888).下面是一个计算inSampleSize
的参考方法:
public static 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 halfHeight = height / 2;
final int halfWidth = width / 2;
// 计算最大inSampleSize,使其始终是2的倍数
// 保证图片尺寸是大于要求尺寸
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
代码很简单,此处就不赘述了。
下面是调用这一方法的代码:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
需要注意的是,你必须根据图片的来源来调整decode方法的类型。