Bitmap使用详解

用到的图片不仅仅包括.png、.gif、.9.png、.jpg和各种Drawable系对象,还包括位图Bitmap

图片的处理也经常是影响着一个程序的高效性和健壮性。

Bitmap

  • Bitmap代表一张位图,扩展名可以是.bmp或者.dib。
  • 由点(像素)组成,每个点可以由多种色彩表示
  • Bitmap是图像处理最重要的中转类之一。
  • 用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

为什么不直接用Bitmap传输?
位图文件虽好,但是非压缩格式,占用较大存储空间。

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 static Bitmap createBitmap(Bitmap src)  //以src为原图生成不可变得新图像
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) //以src为原图,创建新的图像,指定新图像的高宽以及是否可变。
public static Bitmap createBitmap(int width, int height, Config config) //创建指定格式、大小的位图
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) //以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。
public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的图片格式以及画质,将图片转换为输出流。

format:压缩图像的格式,如Bitmap.CompressFormat.PNG或+ Bitmap.CompressFormat.JPEG
quality:画质,0-100.0表示最低画质压缩,100以最高画质压缩。对于PNG等无损格式的图片,会忽略此项设置。
stream: OutputStream中写入压缩数据。
return: 是否成功压缩到指定的流。

BitmapFactory

用于从不同的数据源(如文件、输入流、资源文件、字节数组、文件描述符等)解析、创建Bitmap对象

public static Bitmap decodeFile(String pathName, Options opts)  //从文件读取图片
public static Bitmap decodeFile(String pathName)
public static Bitmap decodeStream(InputStream is)  //从输入流读取图片
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeResource(Resources res, int id)  //从资源文件读取图片
public static Bitmap decodeResource(Resources res, int id, Options opts)
public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //从数组读取图片
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd) //从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

BitmapFactory.Options 类

允许我们定义图片以何种方式如何读到内存。

public boolean inJustDecodeBounds //如果设置为true,不获取图片,不分配内存,但会返回图片的高度宽度信息。
public int inSampleSize //图片缩放的倍数
这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4。
public int outWidth //获取图片的宽度值
public int outHeight //获取图片的高度值
表示这个Bitmap的宽和高,一般和inJustDecodeBounds一起使用来获得Bitmap的宽高,但是不加载到内存。
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,在这个模式下,一个像素点占用4bytes空间,一般对透明度不做要求的话,一般采用RGB_565模式,这个模式下一个像素点占用2bytes。
public String outMimeType  //设置解码图像
public boolean inPurgeable //当存储Pixel的内存空间在系统内存不足时是否可以被回收
public boolean inInputShareable  //inPurgeable为true情况下才生效,是否可以共享一个InputStream
public boolean inPreferQualityOverSpeed  //为true则优先保证Bitmap质量其次是解码速度
public boolean inMutable  //配置Bitmap是否可以更改,比如:在Bitmap上隔几个像素加一条线段
public int inScreenDensity  //当前屏幕的像素密度

一张图片(BitMap)占用的内存=图片长度图片宽度单位像素占用的字节数。

推荐阅读:Android - Bitmap-内存分析

Bitmap应用

构造Bitmap对象

  • 通过Bitmap的静态方法static Bitmap createBitmap()
  • 通过BitmapFactory工厂类的static Bitmap decodeXxx()

加载方式

  • Resource资源加载
  • 本地(SDcard)加载
  • 网络加载
  • 从二进制数据读取图片

注意事项:

本地(SDcard)加载

decodeFileDescriptor比decodeFile高效

查看源码可以知道

  • decodeFile()最终是以流的方式生成bitmap
  • decodeFileDescriptor的源码,可以找到native本地方法decodeFileDescriptor,通过底层生成bitmap
Bitmap bmp = BitmapFactory.decodeFile(imageFile, opts);
 imageView.setImageBitmap(bmp);

替换成

FileInputStream is = = new FileInputStream(path);
bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);

Resource资源加载

Res资源加载方式

建议采用decodeStream代替decodeResource。

因为BitmapFactory.decodeResource 加载的图片可能会经过缩放,该缩放目前是放在 java 层做的,效率比较低,而且需要消耗 java 层的内存。因此,如果大量使用该接口加载图片,容易导致OOM错误,BitmapFactory.decodeStream 不会对所加载的图片进行缩放,相比之下占用内存少,效率更高。

这两个接口各有用处,如果对性能要求较高,则应该使用 decodeStream;如果对性能要求不高,且需要 Android 自带的图片自适应缩放功能,则可以使用 decodeResource。

推荐阅读:[BitmapFactory.decodeResource加载图片缩小的原因及解决方法

Assets资源加载方式

InputStream in = this.getAssets().open("ic_launcher");
Drawable da = Drawable.createFromStream(in, null);
Bitmap mm = BitmapFactory.decodeStream(in);

扩展

view转Bitmap
public static Bitmap convertViewToBitmap(View view, int bitmapWidth, int bitmapHeight){
    Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
    view.draw(new Canvas(bitmap));
    return bitmap;
}

将控件转换为bitmap
public static Bitmap convertViewToBitMap(View view){
    // 打开图像缓存
    view.setDrawingCacheEnabled(true);
    // 必须调用measure和layout方法才能成功保存可视组件的截图到png图像文件
    // 测量View大小
    view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    // 发送位置和尺寸到View及其所有的子View
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    // 获得可视组件的截图
    Bitmap bitmap = view.getDrawingCache();
    return bitmap;
}
public static Bitmap getBitmapFromView(View view){
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable = view.getBackground();
    if (bgDrawable != null)
        bgDrawable.draw(canvas);
    else
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}
几何变换

canvas和Matrix可对Bitmap进行旋转、放缩、平移、切错等操作

对 bitmap 进行裁剪

可以用Bitmap.onCreateBitmap、Canvas的clipRect和clipPath等等方式

推荐阅读:android自定义View学习4--图像剪切与变换

Bitmap | Drawable | InputStream | Byte[ ] 之间进行转换

Drawable转化成Bitmap

blic static Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

Bitmap转换成Drawable

public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
        Drawable drawable = new BitmapDrawable(resources, bm);
        return drawable;
    }

Bitmap的内存优化

及时回收Bitmap
捕获异常

对初始化Bitmap对象过程中可能发生的OutOfMemory异常进行了捕获。如果发生了OutOfMemory异常,应用不会崩溃,而是得到了一个默认的Bitmap图。

缓存通用的Bitmap对象

如果不进行缓存,尽管看到的是同一张图片文件,但是使用BitmapFactory类的方法来实例化出来的Bitmap,是不同的Bitmap对象。缓存可以避免新建多个Bitmap对象,避免内存的浪费。

压缩图片

如果图片像素过大,使用BitmapFactory类的方法实例化Bitmap的过程中,需要大于8M的内存空间,就必定会发生OutOfMemory异常。
可以将图片缩小,以减少载入图片过程中的内存的使用,避免异常发生。

合理选择Bitmap的像素格式
使用矩阵

推荐阅读:
Bitmap详解与Bitmap的内存优化

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343