0. 效果图
1. 基本概念
Bitmap(位图)是Android系统中图像处理最重要的类之一。
Bitmap可以获取到图片的信息:高和宽的像素值以及总的像素值、alpha像素值等。
Bitmap获取到图片信息后,可以对其进行裁剪、缩放等操作。
Bitmap可以通过一个Bitmap创建新的Bitmap。
总之:Bitmap为我们程序员提供了对图像文件的操作支持。(就像File类为我们提供了对本地本件的操作支持一样)
2. 高效加载应用场景
- 假设通过ImageView来显示图片,但是很多时候ImageView并没有图片的原始尺寸那么大,这个时候把整个图片加载进来后再设给ImageIiew根本没有必要,因为ImageView没有办法显示原始的图片。
- 通过BitmapFactory.Options就可以按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存占用从而在一定程度上避免OOM,提高了Btimap加载时的性能。
- BitmapFactory提供的加载图片的四类方法都支持BitmapFactory.Options参数,通过四类加载方式可以很方便的对一个图片进行采样缩放。
3. 高效加载的核心思想
- 采用BitmapFactory.Options来加载所需尺寸的图片。
4. 四种Bitmap的加载方式
- decodeFile:从文件系统中加载
- decodeResource: 从资源中加载
- decodeStream:从输入中流加载
- decodeByteArray:从字节数组中加载
5. 具体分析高效加载
- 通过BitmapFactory.Options来缩放图片,主要是用到了它的inSampleSize参数,即采样率。
- 当inSampleSize为1时,采样后的图片大小为图片的原始大小;当inSampleSize大于1时,比如为2,那么采样后的图片其宽/高均为原图大小的1/2,而像素数为原图的1/4,其占有的内存大小也为原图的1/4.
- 拿一张1024 x 1024像素的图片来说,假定采用ARG8888格式存储,那么占有的内存为1024 x 1024 x 4,即4M(ARG8888 代表每个像素占8个字节,也就是1个byte,1024个byte等于1kb,1024kb是1M),如果inSampleSize为2,那么采样后的图片其内存占有只有512 x 512 x 4,即1MB.
- 可以发现采样率inSampleSize必须是大于1的整数,图片才会有缩小的效果,并且采样率同时作用于宽/高,这将导致缩放后的图片以采样率的2次方形式递减,即缩放比例为1/(inSampleSize 的2次方),比如inSampleSize为4,那么缩放比例就是1/16.
- 有一种特殊的情况:inSampleSize小于1时,其作用相当于1,即无缩放效果。
6. 采样率的开发建议
- inSampleSize的取值应该总是2的指数,比如1、2、4、8、16,等等。如果外界传递给系统的inSampleSize不为2的指数,那么系统会向下取整并选择一个最接近的2的指数来代替,比如3,系统会选择2来代替,当然由于国内手机的私人订制,并非所有Android版本上都成立,因此把它当作一个开发建议即可。
7. 获取采样率的步骤
步骤:
- 将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。
- 从BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidth和outHeight参数。
- 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize
- 将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。
注意:
- InJustDecodeBounds参数,当此参数设置为true时,Bitmap只会解析图片的原始宽/高信息,并不会去真正地加载图片。
- 这个时候BitmapFactory获取的图片宽和高的信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的drawable目录下或者运行在不同屏幕密度的设备上,都可能导致BitmapFactory获取到不同的结果。
8. 代码实现
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calulateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static int calulateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
if (reqWidth == 0 || reqHeight == 0) {
return 1;
}
int outHeight = options.outHeight;
int outWidth = options.outWidth;
int inSampleSize = 1;
if (outHeight > reqHeight || outWidth > reqWidth) {
int halfHeight = outHeight / 2;
int halfWidth = outWidth / 2;
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.testbitmap, 300, 300);
imageView.setImageBitmap(bitmap);
说明:创建drawable-xxxhdpi文件夹,文件夹下放一张3840*2160的图片,然后我们用bitmap来加载为300x300的图片。如文章开头图片。