Android自定义控件(八)——详解创建bitmap的方式

网友投稿 1109 2022-05-29

本文目录

什么是Bitmap

Bitmap格式

它是如何存储每个像素点的

如何进行压缩防止OOM

创建Bitmap

BitmapFactory创建

BitmapFactory.Options参数详解

直接通过Bitmap创建

什么是Bitmap

Bitmap是绘图中非常重要的概念,在我们前面自定义的所有View中,他们的画布Canvas说到底都其实是Bitmap,我们先来看看我们常用的代码片段:

Bitmap bgBitmap=Bitmap.createBitmap(getWidth(),getHeight(),Bitmap.Config.ARGB_8888);//创建一个新空白位图 Canvas canvasBg=new Canvas(bgBitmap); canvasBg.drawColor(Color.RED);

1

2

3

这段代码中,我们先利用bitmap创建一个空白位图,然后在利用Bitmap在创建一个Canvas对象,后续的所有操作其实都是绘制在这个bitmap上的。其实这也间接说明了,bitmap就是由一个个像素点构成的,你可以通过Canvas绘制任何东西在这款bitmap上。

Bitmap格式

既然Bitmap是由一个一个像素点构成的,那么就我们需要知道两个概念:

1.它是如何存储每个像素点的

2.如何进行压缩防止OOM

它是如何存储每个像素点的

一张位图所占用的内存=图片的长度(px)*图片的宽度(px)*一个像素占用的字节数。

而这里一个像素所占用的字节数,就是上面代码中最后一个参数Bitmap.Config.ARGB_8888。

其中A代表透明度,R代表红色,G代表绿色,B代表蓝色。这个参数其实有四种,分别是:

1.ALPHA_8:表示8位的alpha位图,表示只存储图片的透明度,不存储颜色值,一个像素占8位,也就是一个字节。

2.ARGB_4444:表示16位的ARGB位图,即透明度A,红色R,绿色G,蓝色B各占4位,一个像素点占4+4+4+4=16位,2个字节。

3.ARGB_8888:这个是我们最常用的,表示32位的ARGB位图,即透明度A,红色R,绿色G,蓝色B各占8位,一个像素点占8+8+8+8=32位,4个字节。

4.RGB_565:表示16位的RGB位图,没有透明度值,R红色占5位,G绿色占6位,B蓝色占5位,一个像素点占5+6+5=16位,2个字节。

上面的四种格式中,ARGB_444因为画质惨不忍睹,所以在API13中已经弃用,而ARGB_8888一个颜色占了8位,所以画质最高,但如果你的项目中对透明度没什么要求的画,建议使用RGB_565,能节省一半的内存开销。

如何进行压缩防止OOM

如果有需要将bitmap存储在硬盘上,那么必然会考虑到压缩的问题,所以Android也给我们提供了压缩的方案,分别是JPEG,PNG以及WEBP三种。

1.CompressFormat.JPEG:采用JPEG压缩算法,这是一种有损压缩,而且不支持透明度。

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

3.CompressFormat.WEBP:这种提供了有损压缩和无损压缩两种,在API14到API17中,是一种有损压缩格式,不支持透明度,在API18以后是无损压缩,支持透明度。在相同质量下,WEBP比JPEG小40%,比PNG小26%,但是都是牺牲压缩时间来减小文件大小,是JPEG编码长8倍,是PNG的5倍。

创建Bitmap

创建Bitmap的方式有两种,一种是通过BitmapFactory进行创建,一种是bitmap的静态方法。

BitmapFactory创建

我们先来介绍BitmapFactory的创建方式,下面是Bitmap的所有函数:

public static Bitmap decodeResource(Resources res,int id) public static Bitmap decodeResource(Resources res,int id,Options opts) public static Bitmap decodeFile(String pathName) public static Bitmap decodeFile(String pathName,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) public static Bitmap decodeFileDescriptor(FileDescriptor fd,Options opts) public static Bitmap decodeStream(InputStream is) public static Bitmap decodeStream(InputStream is,Options opts) public static Bitmap decodeResourceStream(Resources res,TypedValue value,InputStream is,Rect pad,Options opts)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

从这些函数可以看出来BitmapFactory的功能很强大,可以针对资源,文件,字节数组,FileDescriptor和InputStream数据流解析出对应的Bitmap对象,而且基本上,每个函数都有两种实现,而多的按个参数就是Options opts(这个参数,最后这标题最后讲解)

其实上面的函数基本都很好理解,有一定Java文件操作经验的应该都能很好的使用,这里我们只要先介绍decodeFileDescriptor这个函数,我们先来看看它的使用代码:

String path="/data/data/background.jpg"; FileInputStream is=new FileInputStream(path) Bitmap bitmap=BitmapFactory.decodeFileDescriptor(is.getFD());

1

2

3

这里实现起来也很简单,但有一个疑问,就是这里已经有文件路径,可以直接调用decodeFile()直接生成,为何还要多此一举呢?

小编就直接告诉大家把,因为这样更节约内存空间,而decodeFile比decodeFileDescriptor更容易OOM,至于为什么,直接查看源码大家就知道了,因为decodeFile内部调用了decodeStream,并且调用了如下代码,申请了两次空间:

is=new BufferedInputStream(is,16*1024); tempStorage=new byte[16*2024];

1

2

接着,我们来看看decodeByteArray如何生成Bitmap,假如这张图片来自网络,我们看看先如何获取这个byte[],代码如下:

public class LYJMethod { public static byte[] getImage(String path) throws Exception { URL url=new URL(path); HttpURLConnection httpURLConnection=(HttpURLConnection)url.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setReadTimeout(6000); InputStream is=null; if(httpURLConnection.getResponseCode()==200){ is=httpURLConnection.getInputStream(); byte[] result=readStream(is); is.close(); return result; } return null; } public static byte[] readStream(InputStream is) throws Exception{ ByteArrayOutputStream baos=new ByteArrayOutputStream(); byte[] buffer=new byte[1024]; int len=-1; while((len=is.read(buffer))!=-1){ baos.write(buffer,0,len); } baos.close(); is.close(); return baos.toByteArray(); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

这里可能大家有个疑问,为何明明已经获取了buffer数组,为何还要转换位toByteArray呢?这是因为这个函数所需要的字节数组,以大家想象中的不一样,而是把输入流转换位字节内存输出流的字节数组格式。如果不转换返回结果会一直为null,大家可以在程序中调试试试。

Android自定义控件(八)——详解创建bitmap的方式

这些生成bitmap函数也就这两个函数需要额外注意,其他的基本都差不多,这里就不再赘述了。

BitmapFactory.Options参数详解

接着我们讲解每个函数最后多出来的一个参数Options,这个是设置Bitmap的采样率,通过改变图片的宽度,高度,缩放比例等,以达到减少图片像素的目的。简单的说,通过这个参数,可以更好的控制bimap。下面是这个参数部分成员变量:

public boolean injustDecodeBounds; public int inSampleSize; public int inDensity; public int inTargetDensity; public int inScreenDensity; public boolean inScaled; public Bitmap.Config inPreFerredConfig; public int outWidht; public int outHeight; public String outMimeType;

1

2

3

4

5

6

7

8

9

10

1.injustDecodeBounds:这个参数用于获取图片信息,如果设置为true,表示只获取图片的信息,不分配内存,也就是说调用如下代码后,bitmap为空(默认为false):

BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; Bitmap bitmap=BitmapFactory.decodeRecource(getResources(),R.drawable.backgroud,options);

1

2

3

2.inSampleSize:这个参数用于压缩图片,比如将这个字段设置为4,意思就是从原来图片每4个像素中取一个像素作为返回结果,也就是图片缩小了4倍。

3.inScaled:这个参数用于设置是否缩放图片,我们都知道,我们的安装项目中图片文件夹有很多,如下图所示:

drawable文件夹同样也是如此,比如mdpi为240,其中有一张图片,但其他文件夹都没有对应大小的图片,如果应用到xhdpi的屏幕中,那么这个参数设置true,就将这个240下完美显示的图片,缩放成480在当前屏幕显示, 这样就会造成图片模糊,如果不缩放就设置为false(默认为true)。

4.inDensity,inTargetDensity

这二个参数是相互关联的所以,有必要在一起讲解,同样,我们先来看看这二个参数的意思:

inDensity:用于设置文件所在资源文件夹的屏幕分辨率。

inTargetDensity:表示真实显示的屏幕分辨率。

scale=inTargetDensity/inDensity,这个公式就是手动设置文件所在资源文件夹的分辨率和真实显示屏幕分辨率来指定图片的缩放比例,也就是说,可以通过这两个参数,自己掌握缩放的大小,对于第三个参数理解。

5.inPreferredConfig

这个参数用来设置像素的存储格式,我们在前面已经介绍过了Android手机的4中存储格式,分别为ALPAH_8,RGB_565,ARGB_8888,ARGB_4444,这个参数默认ARGB_8888。

这里详细讲解最常用的6个成员变量,还有3个成员变量,其中outWidht,outHeight就是图片的详细宽高信息,outMimeType就是图片的格式,也就是如果你要获取图片信息直接使用这3个参数即可。inScreenDensity根本用不到,所以这里就不讲解。

直接通过Bitmap创建

同样,我们直接看看Bitmap自带的静态方法:

static Bitmap createBitmap(int widht,int height,Bitmap.Config config) static Bitmao createBitmap(Bitmap src) static Bitmap createBitmap(Bitmap Source,int x int y,int width,int height) static Bitmap createBitmap(Bitmap Source,int x int y,int width,int height,Matrix m,boolean filter) static Bitmap createBitmap(int[] colors,int widht,int height,Bitmap.Config config) static Bitmap createBitmap(int[] colors,int offset,int stride,int width,int height,Bitmap.Config config) static Bitmap createScaledBitmap(Bitmap src,int dstWidht,int dstHeight,boolean filter)

1

2

3

4

5

6

7

最后一个方法我们放大镜那章已经用到过,就是按比例放大缩小图片,这里就不在赘述,同样的createBitmap就是创建一个空白位图,前面也用到过,这里也不在讲解。这里主要讲解第3个到底6个方法。我们先来看一段代码:

//createBitmap(Bitmap Source,int x int y,int width,int height) Bitmap src=BitmapFactory.decodeResource(getResources(),R.drawable,backgroud); Bitmap bitmap=Bitmap.createBitmap(src,src.getWidth()/3,src.getHeight()/3,src.getWidth()/3,src.getHeight()/3)

1

2

3

这个方法用于裁剪图片,其中x,y用于定义裁剪图片的左上角坐标,width和height用于设置裁剪图片的宽高,source就是源图像,这里我们获取了原图片中间的1/9图像。

static Bitmap createBitmap(Bitmap Source,int x int y,int width,int height,Matrix m,boolean filter)

1

第4个方法多了两个参数分别是Matrix m,这个参数用于给裁剪后的图片添加矩阵,boolean filter用于是否给图片添加滤波效果,如果设置为true,能够减少图像中由于噪声引起的突兀的孤立的像素块。比如我这样设置上面的代码:

//createBitmap(Bitmap Source,int x int y,int width,int height) Bitmap src=BitmapFactory.decodeResource(getResources(),R.drawable,backgroud); Matrix matrix=new Matrix(); matrix.setScale(2,1); Bitmap bitmap=Bitmap.createBitmap(src,src.getWidth()/3,src.getHeight()/3,src.getWidth()/3,src.getHeight()/3,matrix,true)

1

2

3

4

5

将是将裁剪后的图片宽度方法两倍。

接着我们再来看看,下面两个方法:

static Bitmap createBitmap(int[] colors,int widht,int height,Bitmap.Config config) static Bitmap createBitmap(int[] colors,int offset,int stride,int width,int height,Bitmap.Config config)

1

2

1.colors:当前图像所对应的每个像素的数组,它的数组长度必须是图片的widht*height。

2.width,height:当前图像的宽高。

3.config:用于指定图像的存储格式,比如:ARGB_888。

这种两个方法使用起来难度非常大,你需要设置每一个像素点的ARGB四个颜色,所以这里不再讲解,我们将结合下一节,详细bitmap的应用,教你如何让图片变得更鲜艳。

Android 数据结构

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Python进阶必备:线程模块threading
下一篇:Git从入门到实战
相关文章