参考:Android Bitmap知识梳理学习
1.关于 Bitmap
在Android中Bitamp
指的就是一张图片,一般是png
和jpeg
格式。
Bitmap
类中有一个enum
类型的Config
,其中有4个值
-
ALPHA_8
8位位图;1 个字节,只有透明度,没有颜色值 -
RGB_565
16位位图;2 个字节,r = 5,g = 6,b = 5,一个像素点 5+6+5 = 16 -
ARGB_4444
16位位图;2 个字节,a = 4,r = 4,g = 4,b = 4,一个像素点 4+4+4+4 = 16 -
ARGB_8888
32 位位图; 4个字节,a = 8,r = 8,g = 8, b = 8,一个像素点 8 + 8 + 8 + 8 = 32
一张 1024 * 1024
像素,采用ARGB8888
格式,一个像素32
位,每个像素就是4
字节,占有内存就是4M
若采用RGB565
,一个像素16
位,每个像素就是2
字节,占有内存就是2M
图像文件的字节数=图像分辨率*颜色深度/8
Glide
加载图片默认格式RGB565
,Picasso
为ARGB8888
,默认情况下,Glide
占用内存会比Picasso
低,色彩不如Picasso
鲜艳,自然清晰度就低
-
BitmaFactory
通过BitmapFactory从文件系统,资源,输入流,字节数组中加载得到一个Bitmap对象。
decodeByteArray()
decodeFile()
decodeResource()
decodeStream()
decodeFileDescriptor()
decodeResourceStream()
BitmapFactory
所有public method
都是静态方法。一共也就6个方法,后两个用的几率不如前4个高
2.Bitmap 的高效加载
核心思想: 利用BitmapFactory.Options
来加载实际所需的尺寸
2.1 BitmapFactory.Options
这个类中只有一个方法requestCancelDecode()
,剩下全是一些常量值
BitmapFactory.Options
缩放图片主要用到inSample
采样率
inSample = 1,采样后图片的宽高为原始宽高
inSample > 1,例如2,宽高均为原图的宽高的1/2
一个采用ARGB8888的1024 * 1024 的图片
inSample = 1
,占用内存就 1024 * 1024 * 4 = 4M
inSample = 2
,占用内存就 512 * 512 * 4 = 1M
缩小规律就是:1 /(inSample ^ 2)
inSample
的值最小为1,低于1时无效的。inSample
的值最好为2,4,8,16,
2的指数。在某些时候,系统会向下取整,例如为3时,系统会用2来代替。2 的指数,可以一定程度上避免图片拉伸变形。
注意:如果压缩后的图片大小小于 ImageView 所期望的大小,那么图片会被拉伸变模糊。
2.2 获取采样率的流程
以读取资源文件为例:
- 创建
BitmapFactory.Options
对象options
- 将
options
的inJustDecodeBounds
参数设为true
,然后使用BitmapFactory.decodeResource(res,resId,options)
加载图片 - 利用
options
取出图片的原始宽高信息,outWidth,outHeight
- 根据采样率的规则并结合实际需要显示的宽高计算出
inSample
- 将
options
的inJustDecodeBounds
参数设为false
,并再次使用BitmapFactory.decodeResource(res,resId,options)
返回采样后的Bitmap
inJustDecodeBounds
设为true
,BitmapFactory
只会解析图片的原始信息,并不会真正的加载图片
BitmapFactory
读取图片的宽高的信息受图片所在drawable
文件夹和设备屏幕本身的像素密度影响。
属性 | 作用 |
---|---|
boolean inJustDecodeBounds |
是否只扫描轮廓 |
int inSample |
采样率 |
Bitmap.Config inPreferredConfig |
格式,色彩模式 |
int outWidth |
bitmap的宽 |
int outHeight |
bitmap的高 |
boolean inDither |
防抖动,默认false |
int inDensity |
像素密度 |
boolean inScaled |
是否可以缩放,默认true |
boolean inMutable |
是否可变,设为ture,decode转换方法返回的结果全部可改变 |
2.3 压缩图片实战
public void intt(){
iv = findViewById(R.id.imageView);
iv.post(new Runnable() {
@Override
public void run() {
int width = iv.getWidth();
int height = iv.getHeight();
iv.setImageBitmap(decodeBitmap(getResources(),R.drawable.datu,width,height));
}
});
}
/**
* 对图片进行压缩
* 通过options把Bitmap的格式设为RGB565。设置成RGB565后,
* 占用内存会少一半,也会减少OOM。
* 个人感觉,除非是专门的图像处理app,
* 大部分时候都可以用RGB565代替ARGB8888,
* 牺牲图像的清晰度,换来一半的占用内存,
* 个人感觉还是比较划算的。并且,清晰度的差别,不同时放在一起比较是不会有很大直观差别的。
* @param res
* @param resId
* @param targetWidth
* @param targetHeight
* @return
*/
private Bitmap decodeBitmap(Resources res , int resId, int targetWidth, int targetHeight){
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //轻量级加载图片(只会解析图片的原始宽高信息)
options.inPreferredConfig = Bitmap.Config.RGB_565;//将Config设为RGB565
BitmapFactory.decodeResource(res,resId,options); //加载图片
options.inSampleSize = calculateInSample(options,targetWidth,targetWidth);//计算 inSampleSize 参数
Log.d("MainActivity", "decodeBitmap: "+options.inSampleSize);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res,resId,options);
}
/**
* 计算inSample
*在calculateInSample()方法中,final int halfWidth = rawWidth / 2这行代码的目的在于防止过度压缩。
* 因为54行已经做了判断,到了57行条件必然满足,
* 当要显示的目标大小和图像的实际大小比较接近时,会产生过度没必要的压缩。
*
* 例如,ImageView的大小为200 * 200,而图像的大小为为250 * 250,如果不进行除以2,到了57行,条件成立,此时inSample的值会再次乘以2,根据缩小规律缩小 = inSample ^ 2,就会又次缩小4倍。
*
* @param options
* @param targetWidth
* @param targetHeight
* @return
*/
private int calculateInSample(BitmapFactory.Options options, int targetWidth, int targetHeight) {
final int rawWidth = options.outWidth;
final int rawHeight = options.outHeight;
int inSample = 2;
if (rawWidth > targetWidth || rawHeight > targetHeight){
final int halfWidth = rawWidth / 2;//为了避免过分压缩 例如 图片大小为 250 * 250 view 200 * 200
final int halfHeight = rawHeight / 2;
while((halfWidth / inSample) >= targetWidth && (halfHeight / inSample) >= targetHeight){
inSample *= 2;
}
}
return inSample;
}
3 Bitmap 方法
主要是查看api文档,想了解下都有哪些方法
-
compress(Bitmap.CompressFormat format, int quality, OutputStream stream)
将bitmap数据质量压缩并转换成流,若format参数设置为了png格式,quality设置无效
-
format
图片的格式,支持3种JPEG,PNG,WEBP -
quality
压缩质量压缩率,0-100,0表示压缩程度最大,100为原质量,但png无效 -
stream
输出流 - 返回值,
boolean
简单使用:
private void initView() {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.cc);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024 * 8);
bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
bitmap =BitmapFactory.decodeByteArray(outputStream.toByteArray(),0,outputStream.size(),options);
Log.e(TAG,"++"+outputStream.size()+","+bitmap.getConfig().name()+","+bitmap.getByteCount()+","+bitmap.getHeight()+","+bitmap.getWidth());
}
在变换成输出流的过程中,把Bitmap
的Config
变为了RGB565
,这里遇到个情况,mipmap
文件夹下的图片,这种方法并不能改变其Config
,一直都是默认ARGB8888
3.2 coyp方法
copy(Bitmap.Config config, boolean isMutable)
拷贝一个Bitmap
的像素到一个新的指定信息配置的Bitmap
-
config
配置信息 -
isMutable
是否支持可改变可写入 - 返回值,
bitmap
,成功返回一个新的bitmap
,失败就null
rivate void initView() {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.m);
bitmap = bitmap.copy(Bitmap.Config.RGB_565,true);
Log.e(TAG,"++"+bitmap.getConfig().name()+","+bitmap.getByteCount()+","+bitmap.getHeight()+","+bitmap.getWidth());
}
方法中isMutable
这个参数暂时不了解具体作用和使用场景
3.3 createBitmap方法
createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
-
source
资源bitmap
-
x
资源bitmap
的第一个像素的x坐标 -
y
资源bitmap
的第一个像素的y坐标 -
m
矩阵 -
filter
是否过滤资源bitmap
- 返回值 一个不可变的
btimap
3.4 其他方法
方法 | 作用 |
---|---|
recycle() |
释放bitmap所占的内存 |
isRecycled() |
判断是否回收内存 |
getWidth() |
得到宽 |
getHeight |
得到高 |
isMutable() |
是否可以改变 |
sameAs(Bitmap other) |
判断两个bitmap大小,格式,像素信息是否相同 |