Android学习之Bitmap类

Bitmap类是安卓中对图像进行处理的最重要的类之一,可以用来获取图片信息,进行颜色变换、剪切、旋转、缩放等操作,并且可以指定保存图片的格式。

1.  Bitmap类中的两个内部枚举类:

(1)Config:用于枚举图片的颜色配置信息(RGBA及对应颜色存储的位数等信息);

(2)CompressFormat:用于设置图片的压缩方式(也就是图片的格式);

Config枚举值包括:

枚举值 含义
Bitmap.Config.ALPHA_8 颜色信息只由透明度组成,仅占8bit
Bitmap.Config.ARGB_4444 颜色信息由RGBA组成,每个部分各占4bit,总共是16bit
Bitmap.Config.ARGB_8888 颜色信息由RGBA组成,每个部分各占8bit,总共是32bit
Bitmap.Config.RGB_565 颜色信息由RGB组成,R占5bit,G占6bit,B占5bit,总共是16bit

开发过程中为了避免图片过大导致内存占用过多产生OOM(Out Of Memory),一般都是使用Bitmap.Config.RGB_565编码图片。

CompressFormat枚举值包括:

枚举值 含义
Bitmap.CompressFormat.JPEG 有损压缩,使用JPEG算法对图片进行压缩,生成图片后缀为jpg、jpeg
Bitmap.CompressFormat.PNG 无损压缩,使用PNG算法对图片进行压缩,生成图片后缀为png
Bitmap.CompressFormat.WEBP 有损压缩,使用WebP算法对图片进行压缩。相比于JPEG,压缩后图片要更小,但是压缩所需时间是JPEG8倍左右

 

2. Bitmap类的实例化及相关操作

Bitmap类可以对图片进行裁剪、旋转、平移、缩放等操作。其具有以下两个静态方法:

Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height);
Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter);

参数1:图片源,bitmap对象实例;

参数2:开始操作图片时,x方向上第一个像素的位置;

参数3:开始操作图片时,y方向上第一个像素的位置;

参数4:操作图片时,x方向像素数;

参数5:操作图片时,y方向像素数;

参数6:Matrix(矩阵)类实例,用于表示对图片进行旋转、平移、缩放等操作的详细信息;

参数7:filter,是否过滤图片源(不太明白,需要实操);

裁剪图片,假设当前有一张图片时50 * 50像素,经过如下裁剪后:

Bitmap imgCrop = Bitmap.createBitmap(sourceImg, 5, 5, 10, 20);

得到的是一张10 * 20像素的图片,裁剪的起点在原图的(5, 5)处。

注:起始位置的像素加上偏移剪切像素值不能超过对应宽高的总像素值(不然就切到外面去了)。

旋转、平移、缩放图片:

在进行如上操作前,我们需要实例化矩阵对象Matrix,在对象中设置我们的具体操作,martix对象实例具有如下方法:

  增量修改 替换修改
旋转 postRotate(degrees) setRotate(degrees)
平移 postScale(xScale, yScale) setScale(xScale, yScale)
缩放 postTranslate(x, y) setTranslate(x, y)

所谓的增量修改,就是post的对应操作会同时生效。而替换修改则是set操作会覆盖上一次设置的操作。

假设现在我们希望顺时针旋转一张图片60度,并在水平方向缩小至0.8,垂直方向上放大至1.2,同时向水平方向正向平移5个像素,垂直方向负向平移10个像素,我们可以这样设置Matrix对象:

Matrix matrix = new Matrix();
matrix.postRotate(60);
matrix.postScale(0.8f, 1.2f);
matrix.postTranslate(5, -10);

Bitmap newImg = Bitmap.createBitmap(sourceImg, 0, 0, source.getWidth(), source.getHeight(), matrix, false);

此外,postScale和postRotate方法还包含多两个参数的重载,多出的参数表示操作的中心坐标(x, y)。

下面是一个完整的例子,包括从资源文件中获取源bitmap对象,到创建原图的副本并绘制:

ImageView sourceImageView = findViewById(R.id.source);
ImageView copyImageView = findViewById(R.id.copy);

// 设置修改后的原图到sourceImageView上
Bitmap sourceImg = BitmapFactory.decodeResource(getResources(), R.drawable.demoImg);
sourceImg.setPixel(64, 64, Color.BLUE);
sourceImageView.setImageBitmap(sourceImg);

// 创建原图的副本,修改并设置到copyImageView上
Bitmap copy = Bitmap.createBitmap(sourceImg.getWidth(), sourceImg.getHeight(), sourceImg.getConfig());  // 获取原图的拷贝bitmap对象

Paint paint = new Paint();
Canvas canvas = new Canvas(copy);

Matrix matrix = new Matrix();
matrix.postRotate(60);
matrix.postScale(0.8, 1.2);
matrix.postTranslate(5, -10);

canvas.drawBitmap(sourceImg, matrix, paint);  // 对原图的修改设置到画布上
copyImageView.setImageBitmap(copy);

 

3. Bitmap实例对象的常用方法

方法 作用
public void recycle() 回收位图占用的内存空间,把位图标记为Dead
public final boolean isRecycled() 判断位图的资源是否已经释放
public final int getWidth() 获取位图的宽度
public final int getHeight() 获取位图的高度
public final boolean isMutable() 获取位图是否是可修改状态
public int getScaledWidth(Canvas canvas) 获取指定密度转换后的位图宽度
public int getScaledHeight(Canvas canvas) 获取指定密度转换后的位图高度
public boolean compress(CompressFormat format, int quality, OutputStream stream) 使用指定的算法压缩图片,可以指定图片质量,指定压缩后图片的输出流

 

4. BitmapFactory类

这个类用于从图片源中实例化bitmap对象,图片源可以是文件、流或字节数组。常用方法如下:

方法 说明
decodeFile(String pathName) 从文件中读取图片
decodeFile(String pathName, Options opts) 同上
decodeFileDescriptor(FileDescriptor fd) 从文件描述符读取图片(通过jni完成,效率比较高)
decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) 同上
decodeStream(InputStream is) 从输入流读取图片
decodeStream(InputStream is, Rect outPadding, Options opts) 同上
decodeResource(Resources res, int id) 从资源文件读取图片
decodeResource(Resources res, int id, Options opts) 同上
decodeByteArray(byte[] data, int offset, int length) 从图片字节数组读取图片
decodeByteArray(byte[] data, int offset, int length, Options opts) 同上

(1)从资源文件中读取图片:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
imageView.setImageBitmap(bitmap);

(2)从文件中以流的形式读取图片:

假设在app的内部存储路径中(/data/data/yourPackageName/files/test.png)有一张图片test.png:

File imageFile = new File(mContext.getFilesDir() + File.separator + "test.png");
FileInputStream imageFileStream = new FileInputStream(imageFile);
Bitmap bitmap = BitmapFactory.decodeStream(imageFileStream);
imageView.setImageBitmap(bitmap);

此外BitmapFactory还可以用来保存bitmap图片到本地,使用compress方法指定压缩方式及写入文件输出流对应的文件路径:

try {
    File targetWritePath = new File(mContext.getFilesDir() + File.separator + "images");
    FileOutputStream stream = new FileOutputStream(targetWritePath);
    BufferedOutputStream bufferedStream = new BufferedOutputStream(stream);
    int quality = 100;  // 0 - 100,100表示质量最高,PNG无损压缩会无视该属性
    bitmap.compress(Bitmap.CompressFormat.PNG, quality, bufferedStream);
    bufferedStream.flush();
    bufferedStream.close();

    // 释放bitmap占用的内存空间
    if (bitmap != null && !bitmap.isRecycled()) {
        bitmap.recycle();
        bitmap = null;
    }
} catch (IOException e) {
    e.printStackTrace();
}

使用完bitmap后调用recycle还是非常有必要的,但是如果在Activity中正在显示bitmap,不能够回收该对象,可以放到onDestory中进行回收,可以看看这篇博客。

@Override
protected void onDestroy() {
    super.onDestroy();

    if (sourceImg != null && !sourceImg.isRecycled()) {
        sourceImg.recycle();
    }

    if (copyImg != null && !copyImg.isRecycled()) {
        copyImg.recycle();
    }
}

 

5. BitmapFactory.Options

BitmapFactory.Options是Bitmap的内部类,通过实例化这个类,我们可以在decode获取bitmap实例的时候,传入options对象,指定相关属性,支持的部分属性如下:

属性 说明
public boolean inJustDecodeBounds true,若在decode时设置为true,不会分配内存获取图片完整数据,但是能够返回图片的宽高等信息,此时返回的bitmap对象是null
public int inSampleSize 指定图片的缩放倍数
public int outWidth 图片的宽度值
public int outHeight 图片的高度值
public int inDensity 用于位图的像素压缩比
public int inTargetDensity 用于目标位图的像素压缩比(要生成的位图)
public byte[] inTempStorage 创建临时文件,存储图片
public boolean inScaled 设置为true时会对图片压缩,从inDensity到inTargetDensity
public boolean inDither 若为true,解码器尝试抖动解码
public Bitmap.Config inPreferredConfig 设置图片色彩模式,默认为ARGB_8888(一个像素占32bit),也可以使用RGB_565(一个像素占用16bit)
public String outMimeType 设置解码的Mime类型
public boolean inPurgeable inPurgeable为true情况下才生效,是否可以共享一个InputStream
public boolean inPreferQualityOverSpeed 设置为true优先保证解码质量,其次才是解码速度
public boolean inMutable 解码的bitmap对象是否可修改,例如增加像素等
public int inScreenDensity 当前屏幕的像素密度

下面是一个例子,当我们只想要获取图片的宽高等信息,而又不想读出整个图片,占用过多内存时,可以这样写:

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opts);
Log.d(TAG, "image width is " + opts.outWidth + " image height is " + opts.outHeight);

如果读取到图片宽高发现图片过大时,可以对图片进行压缩:

opts.inSampleSize = 2;  // 读取的图片宽高分别为原来的1/2,图片总大小是原来的1/4
opts.inJustDecodeBounds = false;  // 设置回false
Bitmap bitmap = BitmapFactory.decodeFile(path, opts);  // 此时读取的图片是压缩过的

本文参考自:Bitmap详解与Bitmap的内存优化

你可能感兴趣的:(Android学习之Bitmap类)