Android Bitmap的简单理解和使用

  • Android Bitmap
    • 一.Bitmap的定义
    • 二.Bitmap的格式
      • 2.1 存储格式
      • 2.2 压缩格式
    • 三.Bitmap创建方法
      • 3.1 BitmapFactory
        • 3.1.1、 Bitmap.Options类
      • 3.2 Bitmap静态方法
      • 3.3 创建Bitmap的总结
    • 四.常见函数
      • 4.1 函数及其参数
      • 4.2 常用操作
    • 五.常见问题
      • 5.1 Bitmap与Canvas,View,Drawable的关系
      • 5.2 使用Bitmap如何造成内存溢出的?
      • 5.3怎么解决或者避免Bitmap内存溢出?
      • 5.4 Bitmap与Drawable的转换
        • 5.4.1 Drawable转换成Bitmap
        • 5.4.2 Bitmap转换成Drawable
  • 参考

Android Bitmap

一.Bitmap的定义

Bitmap是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

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

  • 1.给ImageView设置背景
  • 2.当做画布来使用

分别对应下面两个方法

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

二.Bitmap的格式

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

  • 第一:如何存储每个像素点?
  • 第二:怎么压缩像素点?

Bitmap 中有两个内部枚举类:ConfigCompressFormatConfig 是用来设置颜色配置信息的,CompressFormat 是用来设置压缩方式的

2.1 存储格式


2.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创建方法

我们如何创建一个 Bitamap 对象呢?Google 给我们提供了两种方式:

  • 1、Bitmap 的静态方法 createBitmap(XX)
  • 2、BitmapFactory 的 decodeXX 系列静态方法

3.1 BitmapFactory

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


decodeFile、 decodeResource、decodeStream和decodeByteArray,分别用于支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap对象,其中decodeFiledecodeResource又间接调用了decodeStream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native 方法。

3.1.1、 Bitmap.Options类

如何高效地加载bitmap?

通过BitmapFactory.Options按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存占用从而在一定程度上避免OOM,提高了Bitmap 加载时的性能。

采样率解释:
BitmapFactory提供的加载图片的四类方法都支持BitmapFactory.Options参数
通过BitmapFactory.Options 来缩放图片,主要用到了inSampleSize参数,即采样率

  • inSampleSize为1时,采样后的图片大小为图片的原始大小;
  • inSampleSize大于1时,比如为2,那么采样后的图片其宽/高均为原图大小的1/2,而像素数为原图的1/4,其占有的内存大小也为原图的1/4。

拿一张1024×1024像素的图片来说,假定采用ARGB8888格式存储,那么它占有的内存为1024×1024×4即4MB,如果inSampleSize为2,那么采样后的图片其内存占用只有512×512×4,即1MB。

采样率同时作用于宽/高,这将导致缩放后的图片大小以采样率的2次方形式递减,即缩放比例为1/ (inSampleSize的2次方)

  • 比如inSampleSize为4,那么缩放比例就是1/16。
  • 有一种特殊情况,那就是当inSampleSize 小于1时,其作用相当于1,即无缩放效果。

参数说明:

举例说明

try {FileInputStream fis = new FileInputStream(filePath);BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;// 设置inJustDecodeBounds为true后,再使用decodeFile()等方法,并不会真正的分配空间,//即解码出来的Bitmap为null,但是可计算出原始图片的宽度和高度,即options.outWidth和options.outHeightBitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);float srcWidth = options.outWidth;float srcHeight = options.outHeight;int inSampleSize = 1;if (srcHeight > height || srcWidth > width) {if (srcWidth > srcHeight) {inSampleSize = Math.round(srcHeight / height);} else {inSampleSize = Math.round(srcWidth / width);}}options.inJustDecodeBounds = false;options.inSampleSize = inSampleSize;return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
} catch (Exception e) {e.printStackTrace();
}

过程说明:

  • (1) 将BitmapFactory.OptionsinJustDecodeBounds参数设为true并加载图片。该参数为true时,BitmapFactory只会解析图片的原始宽高信息,不会去真正加载图片,同时获取到的信息和图片的位置与程序运行的设备有关
  • (2) 从BitmapFactory.Options 中取出图片的原始宽高信息,它们对应于outWidthoutHeight参数。
  • (3) 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize.
  • (4) 将BitmapFactory.OptionsinJustDecodeBounds 参数设为false, 然后重新加载图片。

3.2 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)

这些方法大致可以分为三类:

  • 1、根据已有的Bitmap来创建新Bitmap
/**
* 通过矩阵的方式,返回原始 Bitmap 中的一个不可变子集。新 Bitmap 可能返回的就是原始的 Bitmap,也可能还是复制出来的。
* 新 Bitmap 与原始 Bitmap 具有相同的密度(density)和颜色空间;
*
* @param source   原始 Bitmap
* @param x        在原始 Bitmap 中 x方向的其起始坐标(你可能只需要原始 Bitmap x方向上的一部分)
* @param y        在原始 Bitmap 中 y方向的其起始坐标(你可能只需要原始 Bitmap y方向上的一部分)
* @param width    需要返回 Bitmap 的宽度(px)(如果超过原始Bitmap宽度会报错)
* @param height   需要返回 Bitmap 的高度(px)(如果超过原始Bitmap高度会报错)
* @param m        Matrix类型,表示需要做的变换操作
* @param filter   是否需要过滤,只有 matrix 变换不只有平移操作才有效
*/
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,@Nullable Matrix m, boolean filter) 
  • 2、通过像素点数组创建空的Bitmap
/*** * 返回具有指定宽度和高度的不可变位图,每个像素值设置为colors数组中的对应值。* 其初始密度由给定的确定DisplayMetrics。新创建的位图位于sRGB 颜色空间中。* @param display  显示将显示此位图的显示的度量标准* @param colors   用于初始化像素的sRGB数组* @param offset   颜色数组中第一个颜色之前要跳过的值的数量* @param stride   行之间数组中的颜色数(必须> = width或<= -width)* @param width    位图的宽度* @param height   位图的高度* @param config   要创建的位图配置。如果配置不支持每像素alpha(例如RGB_565),* 那么colors []中的alpha字节将被忽略(假设为FF)*/public static Bitmap createBitmap(@NonNull DisplayMetrics display,@NonNull @ColorInt int[] colors, int offset, int stride,int width, int height, @NonNull Config config) 
  • 3、 创建缩放的Bitmap
/**
* 对Bitmap进行缩放,缩放成宽 dstWidth、高 dstHeight 的新Bitmap
*/
public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,boolean filter)

3.3 创建Bitmap的总结

  • 1.加载图像可以使用BitmapFactory和Bitmap.create系列方法
  • 2.可以通过Options实现缩放图片,获取图片信息,配置缩放比例等功能
  • 3.如果需要裁剪或者缩放图片,只能使用create系列函数
  • 4.注意加载和创建bitmap事通过try catch捕捉OOM异常

四.常见函数

4.1 函数及其参数

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

综合案例演示

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) {}});

4.2 常用操作

1、裁剪、缩放、旋转、移动

Matrix matrix = new Matrix();
// 缩放
matrix.postScale(0.8f, 0.9f);
// 左旋,参数为正则向右旋
matrix.postRotate(-45);
// 平移, 在上一次修改的基础上进行再次修改 set 每次操作都是最新的 会覆盖上次的操作
matrix.postTranslate(100, 80);
// 裁剪并执行以上操作
Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);

2、保存与释放

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
File file = new File(getFilesDir(),"test.jpg");
if(file.exists()){file.delete();
}
try {FileOutputStream outputStream=new FileOutputStream(file);bitmap.compress(Bitmap.CompressFormat.JPEG,90,outputStream);outputStream.flush();outputStream.close();
} catch (FileNotFoundException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();
}
//释放bitmap的资源,这是一个不可逆转的操作
bitmap.recycle();

3、图片压缩

public static Bitmap compressImage(Bitmap image) {if (image == null) {return null;}ByteArrayOutputStream baos = null;try {baos = new ByteArrayOutputStream();image.compress(Bitmap.CompressFormat.JPEG, 100, baos);byte[] bytes = baos.toByteArray();ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);Bitmap bitmap = BitmapFactory.decodeStream(isBm);return bitmap;} catch (OutOfMemoryError e) {e.printStackTrace();} finally {try {if (baos != null) {baos.close();}} catch (IOException e) {e.printStackTrace();}}return null;
}

五.常见问题

5.1 Bitmap与Canvas,View,Drawable的关系

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

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

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

  • 1.我们可以对位图进行压缩,压缩手段有PNG,JPEG,WEBP
  • 2.对不使用的Bitmap一定要及时回收。
  • 3.在创建Bitmap时使用try catch步骤OOM异常,使程序更健壮,即使发生了OOM也不会闪退,造成不好的使用体验.

5.4 Bitmap与Drawable的转换

5.4.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;  }

5.4.2 Bitmap转换成Drawable

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

参考

1、那些关于Bitmap图片资源优化的小事
2、Bitmap史上最详细全解
3、Android Bitmap详解
4、Android Bitmap的内存大小是如何计算的?
5、Android Bitmap 详解:关于 Bitamp 你所要知道的一切

Android:安卓学习笔记之Bitmap的简单理解和使用相关推荐

  1. Android:安卓学习笔记之navigation的简单理解和使用

    Android navigation的简单理解和使用 1 .基本概念 1.1.背景 1.2.含义 2.组成 2.1.Navigation graph 2.2.NavHostFragment 2.3.N ...

  2. 安卓开发Android studio学习笔记12:读取解析XML(案例演示)

    Android studio学习笔记 第一步:配置Student.XML 第二步:配置activity_main.xml 第三步:配置student.xml 第四步:配置Student用户类 第五步: ...

  3. Android:日常学习笔记(8)———探究UI开发(5)

    Android:日常学习笔记(8)---探究UI开发(5) ListView控件的使用 ListView概述 A view that shows items in a vertically scrol ...

  4. Android Studio --- [学习笔记]RadioButton、CheckBox、ImageView、ListView、TCP的三次握手

    说明 源代码 在2.x里有TCP的三次挥手与四次握手,先对它进行简单的回答(百度).预计在下一篇里,会继续说明TCP 接上一篇: Android Studio - > [学习笔记]Button. ...

  5. 2020年安卓学习笔记目录

    文章目录 一.讲课笔记 二.安卓案例 三.安卓实训项目 四.学生安卓学习博客 五.安卓课后作业 (一)界面设计练习 1.制作登录界面 2.制作部队管理界面 3.制作灭火救援界面 4.制作交付界面 5. ...

  6. java/android 设计模式学习笔记(1)--- 单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  7. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  8. Android:日常学习笔记(8)———探究UI开发(2)

    Android:日常学习笔记(8)---探究UI开发(2) 对话框 说明: 对话框是提示用户作出决定或输入额外信息的小窗口. 对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件. 提示 ...

  9. Android Studio --- [学习笔记]TCP(第2弹)、GridView、ScrollView

    说明 这篇主要接上一篇Android Studio - > [学习笔记]RadioButton.CheckBox.ImageView.ListView.TCP的三次握手 对上面回答的细解,并用J ...

最新文章

  1. 【转】第一类Stirling数和第二类Stirling
  2. Flex4之与后台服务器通信方式:URLRequest+URLLoader【JAVA方式】
  3. AlphaGo之父亲授深度强化学习十大法则
  4. macOS安装Maven_IDEA集成Maven
  5. 2020年度中国生命科学十大进展揭晓
  6. Matlab--三种工具绘制errorbar图
  7. Machine Learning and Data Science 教授大师
  8. golang之web编程入门
  9. Qt高级编程完整源码
  10. Intellij IDEA远程debug教程实战和要点总结
  11. zend studio html乱码,解决Eclipse/Zend Studio编辑xml/html乱码问题
  12. datastore java_Android 使用DataStore存储数据
  13. Unity 3D 热更新之基于 Asset Bundle Browser 的 AssetBundle包
  14. 【OUC深度学习入门】第4周学习记录:MobileNetV1, V2, V3
  15. 基于51单片机十字路口交通灯_5s黄灯闪烁
  16. 荣耀note10鸿蒙os,荣耀Note20最新确认:7000mAh+双6400万+鸿蒙OS,售价感人
  17. wps怎么恢复成单页_我告诉你文档两页怎么变成单页
  18. 权限管理后端篇(一)之创建权限管理表引入knife4j和Druid数据源
  19. 十万部冷知识:“沙特”为什么能赢“阿根廷”
  20. vue3.x 使用jsplumb进行拖拽连线

热门文章

  1. Android相机开发那些坑
  2. 计组复习(四):cache,虚拟内存,页表与TLB
  3. 解决:Command line is too long. Shorten command line for xxx or also for Application default configurat
  4. Ubuntu系统下安装ssh的方法
  5. 唤醒手腕Python全栈工程师学习笔记(并发编程篇)
  6. 华为mate40 pro 什么时候上市 外观 参数 配置曝光
  7. 基础篇 | 03 | 如何提升用户体验
  8. 计算机维修看图,计算机在看图的时候,图像识别在看什么?
  9. 三极管开关电路_【硬见小百科】一种三极管开关电路设计(多图)
  10. android百度地图定位自定义图标,Android应用开发之android 百度地图自定义圆,更改默认图标等常用方法...