作者 | ༺ IF ༻

责编 | 王晓曼

出品 | CSDN博客

Bitmap的相关使用

关于 Bitmap ,之前以为它和 Drawable 差不多,就是一种图片,直到泪水打湿了我胸前的红领巾,我决定整理一波关于 Bitmap 的姿势!

Bitmap相关的使用主要有两种:

1.给 ImageView 设置背景;

2.当做画布来使用 。

分别对应下面两个方法:

 imageView.setImageBitmap(Bitmap bm);Canvas canvas = new Canvas(Bitmap bm)

Bitmap的格式

我们知道 Bitmap 是位图,是由像素点组成的,这就涉及到两个问题:

第一:如何存储每个像素点?

第二:怎么压缩像素点?

1、存储格式

Bitmap有四种存储方式,对应Bitmap.Config中的四个常量:

  • ALPHA_8:只存储透明度,不存储色值,1个像素点占1个字节;

  • ARGB_4444:ARGB各用4位存储,1个像素点16位占2个字节;

  • ARGB_8888:ARGB各用8位存储,1个像素点32位占4个字节;

  • RGB_565:只存储色值,不存储透明度,默认不透明,RGB分别占5,6,5位,一个像素点占用16位2个字节。

一般情况下,我们不会使用 ALPHA_8 ,他只存储透明度,没啥用处。对于 ARGB_4444 ,它的画质又太感人了,ARGB_8888 画质高但是占内存, RGB_565 还行,就是不可以设置透明度。

注意以下三点即可:

  • 一般情况下用ARGB_8888格式存储Bitmap;

  • ARGB_4444画面惨不忍睹,被弃用;

  • 假如对图片没有透明度要求,可以使用RGB_565,比ARGB_8888节省一半的内存开销。

2、压缩格式

我们不妨来计算一下,如果一张和手机屏幕大小一样的Bitmap图片,采用ARGB_8888格式存储需要多大的内存!

按照1024*768的屏幕大小来计算,每个像素需要32位也就是4个字节:

result = 1024*768*32B=25165824B=24MB

一张手机屏幕大小的 Bitmap 图片竟然要 24M ? 那就不奇怪我的 APP 为什么一直闪退了,只不过用 for 循环创 建了几十个用在滑动列表里面。

所以我们必须要对图片进行压缩呀,压缩格式使用枚举类 Bitmap.CompressFormat 中,有以下三种:

  • Bitmap.CompressFormat.JPEG :采用 JPEG 压缩算法,是一种有损压缩格式,会在压缩过程中改变图像原本质量,画质越差,对原来的图片质量损伤越大,但是得到的文件比较小,而且 JPEG 不支持透明度,当遇到透明度像素时,会以黑色背景填充。

  • Bitmap.CompressFormat.PNG :采用 PNG 算法,是一种支持透明度的无损压缩格式。

  • Bitmap.CompressFormat.WEBP : WEBP 是一种同时提供了有损压缩和无损压缩的图片文件格式,在14<=api<=17时,WEBP 是一种有损压缩格式,而且不支持透明度,在 api18 以后 WEBP 是一种无损压缩格式,而且支持透明度,有损压缩时,在质量相同的情况下, WEBP 格式的图片体积比 JPEG 小40%,但是编码时间比 JPEG 长8倍。在无损压缩时,无损的 WEBP 图片比 PNG 压缩小26%,但是 WEBP 的压缩时间是 PNG 格式压缩时间的5倍。

Bitmap创建方法

1、Bitmap.Options

想要创建一个Bitmap有很多种方法,其中很多方法都要求传入一个Bitmap.Options,它是什么呢,有什么作用呢?

这个参数的作用非常大,他可以设置 Bitmap 的采样率,通过改变图片的宽度高度和缩放比例等,以达到减少图片像素数的目的,一言以蔽之,通过设置这个参数我们可以很好的控制显示和使用 Bitmap 。实际开发过程中,可以灵活设置该值,以降低 OOM 发生的概率。

介绍几个重要的成员变量:

  • inJustDecodeBounds:boolean类型,设为true时,无需要把图片加载入内存就可以获取图片的高度,宽度和图片的MIME类型。

高度通过options.outWidth获取 宽度通过options.outHeight获取;

MIME通过options.outMineType获取。

  • inSampleSize:这个字段表示采样率,打个比方说,设置为4,则是从原本图片的四个像素中取一个像素作为结果返回。其余的都被丢弃。可见,采样率越大,图片越小,失真越严重。如何计算采样率呢?看一下这段代码你就会明白:

 public int getSampleSize(BitmapFactory.Options options , int dstWidth,int dstHeight){//dstWidth:表示目前ImageView的宽度//dstHeight:表示目标ImageView的高度//option中获取bitmap图片的信息int  rawWidth = options.outWidth;int  rawHeight = options.outHeight;int sampleSize=1;if(rawWidth>dstWidth||rawHeight>dstHeight){float ratioHeight = (float) (rawHeight/dstHeight);float ratioWidth = (float) (rawWidth/dstWidth);sampleSize = (int) Math.min(rawHeight, ratioWidth);}return sampleSize;}

为了更清楚的介绍下面的知识,先补充几点:

  • 不同名称的资源文件夹是为了适配不同的屏幕分辨率的,当屏幕分辨率与文件所在资源文件夹对应的分辨率相等时,直接使用图片,不需要进行放缩。

  • 当屏幕分辨率与图片所在文件夹所对应的分辨率不同时,会进行缩放,缩放比例是屏幕分辨率/文件夹所对应的分辨率。

  • 从本地文件中加载图片时,不会对图片进行缩放噢。

inScald :这个参数表示,在可以缩放时,是否对当前文件进行放缩,如果设置为 false 就不放缩。设置为 true,则会根据文件夹分辨率和屏幕分辨率进行动态缩放。

inPreferredConfig :这个参数是用来设置像素的存储格式的。

关于 Options 就介绍这几个关键的字段,下面进入重头戏,创建Bitmap。

2、BitmapFactory

BitmapFactory 提供了多种创建 bitmap 的静态方法:

//从资源文件中通过id加载bitmap
//Resources res:资源文件,可以context.getResources()获得
//id:资源文件的id,如R.drawable.xxx
public static Bitmap decodeResources(Resources res,int id)
//第二种只是第一种的重载方法,多了个Options参数
public static Bitmap decodeResources(Resources res,int id,Options opt)
//传入文件路径加载,比如加载sd卡中的文件
//pathName:文件的全路径名
public static Bitmap decodeFile(String pathName);
public static Bitmap decodeFile(String pathName,Options opt);
//从byte数组中加载
//offset:对应data数组的起始下标
//length:截取的data数组的长度
public static Bitmap decodeByteArray(byte[] data,int offset , int length);
public static Bitmap decodeByteArray(byte[] data,int offset , int length,Options opt);
//从输入流中加载图片
//InputStream is:输入流
//Rect outPadding:用于返回矩形的内边距
public static Bitmap decodeStream(InputStream is);
public static Bitmap decodeStream(InputStream is,Rect outPadding,Options opt);
//FileDescriptor :包含解码位图的数据文件的路径
//通过该方式从路径加载bitmap比decodeFile更节省内存,原因不解释了。
public static Bitmap decodeFileDescriptor(FileDescriptor fd);
public static Bitmap decodeFileDescriptor(FileDescriptor fd,Rect outPadding,Options opt);

平时用这些函数都是糊里糊涂的,今天整理了一遍发现其实有规律可寻,也更加清楚了。

3、Bitmap静态方法

//width和height是长和宽单位px,config是存储格式
static Bitmap createBitmap(int width , int height Bitmap.Config config)
// 根据一幅图像创建一份一模一样的实例
static Bitmap createBitmap(Bitmap bm)
//截取一幅bitmap,起点是(x,y),width和height分别对应宽高
static Bitmap createBitmap(Bitmap bm,int x,int y,int width,int height)
//比上面的裁剪函数多了两个参数,Matrix:给裁剪后的图像添加矩阵 boolean filter:是否给图像添加滤波效果
static Bitmap createBitmap(Bitmap bm,int x,int y,int width,int height,Matrix m,boolean filter);
//用于缩放bitmap,dstWidth和dstHeight分别是目标宽高
createScaledBitmap(Bitmap bm,int dstWidth,int dstHeight,boolean filter)

4、创建Bitmap的总结

  • 加载图像可以使用BitmapFactory和Bitmap.create系列方法

  • 可以通过Options实现缩放图片,获取图片信息,配置缩放比例等功能

  • 如果需要裁剪或者缩放图片,只能使用create系列函数

  • 注意加载和创建bitmap事通过try catch捕捉OOM异常

常见函数

1、函数及其参数

copy(Config config,boolean isMutable)
//根据原图像创建一个副本,但可以指定副本的像素存储格式
//参数含义。
//  config:像素在内存中的存储格式,但可以指定副本的像素存储格式
//  boolean isMutable:新建的bitmap是否可以修改其中的像素值
extractAlpha()
//主要作用是从bitmap中获取Alpha值,生成一幅只有Alpha值得图像,存储格式是ALPHA_8
getByteCount()//获取bitmap的字节数
recycle()://不用的bitmap必须要及时回收,以免造成oom
isRecycled()//判断bitmap是否被回收,被收回不可使用会造成crash

2、综合案例演示

        String items[] = {"copy","extractAlpha 1","extractAlpha 2","bitmap大小","recycle","isRecycled()"};ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item,items);adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);spinner.setAdapter(adapter);spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position, long id) {switch (position){case 0://copyBitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.photo);Bitmap copy = bm.copy(Bitmap.Config.ARGB_8888, true);imageView.setImageBitmap(copy);bm.recycle();break;case 1://extractAlpha 不带参数Bitmap bp = BitmapFactory.decodeResource(getResources(), R.drawable.photo);Bitmap alpha = bp.extractAlpha();imageView.setImageBitmap(alpha);bp.recycle();break;case 2://extractAlpha 带参数Bitmap bp1 = BitmapFactory.decodeResource(getResources(), R.drawable.photo);Paint paint = new Paint();BlurMaskFilter blurMaskFilter = new BlurMaskFilter(6, BlurMaskFilter.Blur.NORMAL);paint.setMaskFilter(blurMaskFilter);int[] offsetXY = new int[2];Bitmap alpha1 = bp1.extractAlpha(paint, offsetXY);imageView.setImageBitmap(alpha1);break;case 3://获取bitmap大小Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.photo);Toast.makeText(getApplicationContext(), "图片大小为:"+b.getByteCount()+"字节", Toast.LENGTH_SHORT).show();break;case 4://回收bitmapBitmap b1 = BitmapFactory.decodeResource(getResources(), R.drawable.photo);b1.recycle();if(b1.isRecycled()){Toast.makeText(getApplicationContext(), "已经被回收", Toast.LENGTH_SHORT).show();}//isRecycled()判断是否被回收break;}}@Overridepublic void onNothingSelected(AdapterView<?> parent) {}});

常见问题

1、Bitmap与Canvas,View,Drawable的关系:

  • 我们在创建一个Canvas时,可以传入一个Bitmap,Paint在Canvas上的绘制实际上就是绘制在Bitmap对象上的。

  • 我们自定义空间所显示的View也是通过Canvas中的Bitmap来显示的。

  • Drawable在内存占用和绘制速度这两个非常关键的点上胜过Bitmap。

2、使用Bitmap如何造成内存溢出的?

个人认为,Bitmap 容易造成内存溢出是由于位图较大,一张屏幕大小的ARGB_8888 存储格式的图片竟然有24M,如果有几个这种量级的图片在内存中,并且没有及时回收,那会非常容易造成 OOM。

3、怎么解决或者避免Bitmap内存溢出?

  • 我们可以对位图进行压缩,压缩手段有 PNG,JPEG,WEBP。

  • 对不使用的 Bitmap 一定要及时回收。

  • 在创建 Bitmap 时使用 try catch 步骤 OOM 异常,使程序更健壮,即使发生了 OOM 也不会闪退,造成不好的使用体验。

4、Bitmap与Drawable的转换

(1)Drawable 转换成 Bitmap

public static Bitmap drawableToBitmap(Drawable drawable) {  // 取 drawable 的长宽  int w = drawable.getIntrinsicWidth();  int h = drawable.getIntrinsicHeight();  // 取 drawable 的颜色格式  Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888  : Bitmap.Config.RGB_565;  // 建立对应 bitmap  Bitmap bitmap = Bitmap.createBitmap(w, h, config);  // 建立对应 bitmap 的画布  Canvas canvas = new Canvas(bitmap);  drawable.setBounds(0, 0, w, h);  // 把 drawable 内容画到画布中  drawable.draw(canvas);  return bitmap;  }

(2)Bitmap转换成Drawable

Bitmap bm=Bitmap.createBitmap(xxx);
BitmapDrawable bd= new BitmapDrawable(getResource(), bm);

小结

以前使用 Bitmap 全靠 CV,现在掌握了这么多知识,Bitmap 随便用都不会出现问题,妈妈再也不用担心我内存溢出,太棒了!

版权声明:本文为CSDN博主「༺IF ༻」的原创文章,遵循CC4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/weixin_43927892/article/details/106209563

【END】

更多精彩推荐
☞程序员之痛:六次创业五回失败了
☞Linux 之父怒删工程师提交的补丁,称“太蠢了”网友:怼得好!
☞张一鸣是如何练就字节跳动的
☞性能超越最新序列推荐模型,华为诺亚方舟提出记忆增强的图神经网络
☞DevOps 在移动应用程序开发中扮演什么角色?
☞稳定币经济:十大稳定币简史
你点的每个“在看”,我都认真当成了喜欢

Android Bitmap史上最详细全解 | 原力计划相关推荐

  1. 全网史上最详细全面的Linux下安装mysql客户端服务端

    全网史上最详细全面的Linux下安装mysql客户端服务端Linux下安装mysql 1.上传MySQL5.6的tar包 创建目录: mkdir /usr/local/src/mysql5.6 上传: ...

  2. 史上最详细全中文 Cisco 3560交换机使用手册

    史上最详细全中文 Cisco 3560交换机使用手册 (末尾送交换机安全技术) 目 录 CISCO Catalyst 3560-E系列交换机的功能应用及安全解决方案 3 一.Cisco? Cataly ...

  3. 2020 年 GitHub 上那些优秀 Android 开源库,这里是 Top10! | 原力计划

    作者 | 依然饭特稀西 责编 | 郭芮 出品 | CSDN博客 每过一段时间呀,我都会给大家带来一些从Github上收集的一些开源库,有的是炫酷动效,有的则是实用的工具和类库.2020年有哪些优秀的开 ...

  4. 红米5plus刷android one,史上最详细的魔趣刷机教程(没有之一!!!!)

    本帖最后由 刷机小王子就是我 于 2017-12-12 18:28 编辑 本人亲测过各种ROM,还是觉得魔趣是最好用的!刷机前请看: 刷机是有风险的,有可能会变转,请各位爱搞机小可爱谨慎刷机,如果有什 ...

  5. sketch android xml,史上最详细Sketch Measure讲解

    安装下载安装包 双击Sketch Measure.sketchplugin完成安装 工具栏 一个集成了所有功能图标的工具栏,可以方便操作每一个功能 执行 Plugin > Sketch Meas ...

  6. Python开发之:Django基于Docker实现Mysql数据库读写分离、集群、主从同步详解 | 原力计划...

    作者 | Pythonicc 责编 | 王晓曼 出品 | CSDN博客 简介 1.什么是数据库读写分离 读写分离,基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELET ...

  7. ceph集群和数据库到底是储存数据_Python开发之:Django基于Docker实现Mysql数据库读写分离、集群、主从同步详解 | 原力计划...

    作者 | Pythonicc责编 | 王晓曼出品 | CSDN博客简介1.什么是数据库读写分离读写分离,基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELETE),而从 ...

  8. GitHub 上开源哪家强?| 原力计划

    作者 | 村中少年 责编 | 屠敏 出品 | CSDN 博客 现在有越来越多的公司都参与了开源,其背后有各自的目的所在,姑且不予讨论.本文是从多个方面分析各大公司在开源上的投入情况.由于全世界绝大多数 ...

  9. 史上最详细Docker安装最新版Minio 带详解 绝对值得收藏!!! 让我们一起学会使用minio搭建属于自己的文件服务器!!走上白嫖之路!解决启动了但是浏览器访问不了的原因

    让我们一起学会使用minio搭建属于自己的文件服务器!!走上白嫖之路! WARNING: Console endpoint is listening on a dynamic port (34451) ...

最新文章

  1. 嵌入式java闹钟 实验报告_《Java程序设计》第五次实验实验报告
  2. Apache的Commons Lang和BeanUtils
  3. python软件安装教程-python软件安装
  4. lucene实战--打分算法没有那么难!
  5. C++:MFC SetTimer定时执行某一函数;
  6. 牛顿法中为何出现hessian矩阵
  7. JDK8之Stream新特性
  8. linux一步一脚印---more、less、head、tail
  9. php最简单漂亮的excel导出,php把数据表导出为Excel表的最简单、最快的方法(不用插件)...
  10. 几种简单的社交网站的标志按钮
  11. 编程基本功:顾名思义是可视化编程的要求
  12. 闸机常用通讯协议(韦根,485等),或者开关量输出(继电器)直接控制
  13. 客户需求分析8个维度_电商数据分析的4大思维和8个指标
  14. Python之selenium进阶
  15. 基于Qt的笛卡尔心形表白程序
  16. win10键盘全部没反应_win10笔记本键盘全部没反应 win10键盘全部没反应解决方法...
  17. 最大公约数和最小公倍数,你知道有几种求法吗?
  18. 【Unity俯视角射击】我们来做一个《元气骑士》的完整Demo1
  19. python concat函数 多张表_最全Python数据工具箱:标准库、第三方库和外部工具都在这里了 - Mr_YJY...
  20. android-加固方案对比

热门文章

  1. Linux学习总结(八)-磁盘格式化,挂载,swap扩容
  2. springmvc的作用:
  3. iOS Coding项目片段记录(五)
  4. DucleBox | A Game Engine for OpenGL Programming
  5. 集成Jupyter notebook的工具或平台
  6. 【图像融合】主成分分析PCA
  7. 剑指offer之使数组的奇数在偶数前面
  8. Go语言常用的并发模式(上)
  9. 中国双燃料(DF)发动机市场趋势报告、技术动态创新及市场预测
  10. 中国加热棒行业市场供需与战略研究报告