Android Bitmap知识梳理学习

参考:Android Bitmap知识梳理学习


1.关于 Bitmap

在Android中Bitamp指的就是一张图片,一般是pngjpeg格式。
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加载图片默认格式RGB565PicassoARGB8888,默认情况下,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 获取采样率的流程

以读取资源文件为例:

  1. 创建BitmapFactory.Options对象options
  2. optionsinJustDecodeBounds参数设为true,然后使用BitmapFactory.decodeResource(res,resId,options)加载图片
  3. 利用options取出图片的原始宽高信息,outWidth,outHeight
  4. 根据采样率的规则并结合实际需要显示的宽高计算出inSample
  5. optionsinJustDecodeBounds参数设为false,并再次使用BitmapFactory.decodeResource(res,resId,options)返回采样后的Bitmap

inJustDecodeBounds设为trueBitmapFactory只会解析图片的原始信息,并不会真正的加载图片

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 压缩图片实战

Android Bitmap知识梳理学习_第1张图片
压缩后的图片.png
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());
    }

在变换成输出流的过程中,把BitmapConfig变为了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大小,格式,像素信息是否相同

你可能感兴趣的:(Android Bitmap知识梳理学习)