Android自定义控件开发入门与实战(14)Bitmap

  • decodeFile(String pathName)

根据路径名来加载图片。必须是全路径名 比如 "/data/data/demo.jpg"这样

  • decodeByteArray(byte[] data,int offset ,int length)

data:压缩图像数据的字节数组

offset:图像数据偏移量,用于解码器定位从哪里开始解析

length:字节数,从偏移量开始,指定取多少字节进行解析。

一般是从网络上下载一个InputSteam的流,然后转换成byte,再将其转换成bitmap

  • decodeFileDescriptior(FileDescriptior fd)

通过FileDescriptor对象解析出对应的Bitmap,而FD的一般获取方式是通过构造FileInputStream对象。而FileInputStream又是通过Path拿到的,那为什么我们不直接使用decodeFile直接解析呢?是因为该函数比decodeFile函数更节省内存。

  • decodeStream(InputStream in)

直接传入inputstream就能解析了

3、BitmapFactory.Option

之前在《Android开发艺术探索》中对Option有了印象比较深刻的理解,因为这个参数非常大的影响了图片的存储性能,是降低OOM发生概率的一个比较关键的参数,也是实战、面试中一定会碰到的问题。

但之前理解过很多,所以这里只写之前没有遇到过的。

下面是Options常用的部分成员变量:

public boolean inJustDecodeBounds;

public int inSampleSize;

public int inDensity;

public int inTargetDensity;

public int inScreenDensity;

public boolean inScaled;

public Bitmap.Config inPreferredConfig;

public int outWidth;

public int outHeight;

public String outMimeTye;

其中以in开头的就是设置某某参数,以out开头的就是获取某某参数,比如outWidth就是获取Bitmap的宽。

(1)inJustDecodeBounds 获取图片信息

如果将这个字段设置为true,则表示只解析图片信息,不获取图片、不分配内存,能获取的信息有图片的宽高、MIME。宽高通过outWidth outHeight返回,MIME通过outMimeType返回。

我们在压缩图片时就会经常使用这个字段。一般而言,图片过大时,经常会造成OOM。所以,当图片的尺寸大于我们想要的尺寸时,我们就要进行压缩。这个问题的关键在于,如何不将图片加载到内存中,依然可以知道它的尺寸?而这个参数就很好的完成了这个需求。

我们只需将inJustDecodeBounds设置为true,而不需要将图片加载到内存中,就可以得知它的宽高,然后跟我们想要的尺寸进行对比,进行压缩。

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.avator_xizuka, options);

Log.d(TAG, “bitmap:” + bitmap);

Log.d(TAG, “realwidth:” + options.outWidth + " realheight:" + options.outHeight

  • " mimeType:" + options.outMimeType);

可以得出下面打印信息:

在这里插入图片描述

看到bitmap为null,但是realwidth和realheight和mimeType都是有值的,这说明没有获取到图片,只是解析了它。

解析是不会占用任何内存的。

(2)inSampleSize

就是采样率,balabalabala

这里有一点,比如ImageView是100_100 而图片像素时300_400 这个时候我们要图片缩小3倍还是4倍呢

因为inSampleSize本来就是失真处理,所以尽量让失真效果没那么强,这里就是缩小3倍的。(举个例子,也不能设inSampleSize为3的)

(3)加载一个Bitmap文件究竟需要占多少空间

之前说过 Bitmap所占的内存是 “宽 * 高 * 4” (因为Bitmap现在都默认ARGB_8888了所以为4字节)

但是这个公式是不是就是一定适用于所有Android机 所有情况呢?

其实不是。根本原因是屏幕的适配。

在Android最初版本的时候,设计人员就考虑到了以后Android以后会有许许多多的不同分辨率的屏幕,所以就定制了几个比如drawable-ldpi drawable-mdpi。。。资源文件,当图片符合该分辨率,就不用进行拉伸或者压缩,但是dpi不止那么几个啊,市面上的不同dpi手机太多了,肯定有图片和该这些分辨率文件不一致,所以,Android就会对该Bitmap进行动态的拉伸/压缩。

比如一张图片在SD卡下的原图是600_800 在Activity使用加载该图片后的内存是2MB,然后将它放入到Drawable-xhdpi下,发现它的宽高被拉伸到了960_1200 这个时候通过Activity加载图片就是4MB多了。

总结:

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

  • 当从本地文件SD卡或者内存中加载图片时,不会对图片进行缩放。

(4)inScaled、inDensity、inTargetDensity、inScreenDensity

  • inScaled

只有在一种情况下才会对Bitmap进行拉缩放:就是当图片所在资源文件夹所对应的屏幕分辨率与真实显示的屏幕分辨率不同时。

而这个参数表示,在需要缩放时,是否对当前文件进行缩放。如果inScaled设置为false,则不进行缩放,如果设置为true或者不设置,则会对Bitmap进行动态的缩放。

  • inDensity

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

  • inTargetDensity

表示真实显示的屏幕分辨率

  • inScreenDensity

真实的分辨率,不过该参数很鸡肋,源码中也没有出现过。就很尴尬。

一张图片的缩放比例在这里就是 scale = inTargetDensity / inDensity

所以这两个参数的作用就是:手动设置文件所在资源文件夹的分辨率和真实显示的屏幕分辨率来指定图片的缩放比例。

(5)inPreferredConfig

用来设置存储格式的。可以设置ARGB_8888、RGB_565。。。

4、创建Bitmap方法之二:静态方法

除了用BitmapFactory.decodeXXX函数来加载图片,还可以通过Bitmap自带的静态方法加载图片

就是 createBitmap(...) createScaledBitmap(...)

createBitmap 的构造函数很多,但都容易理解,这里就不再讲解。

来看下createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)

其中Bitmap src表示要缩放的源图像,dstWidth dstHeight表示缩放后的目标宽高,filter表示是否给图像添加滤波效果

到这里,有关创建Bitmap的方法就介绍VAN了

总结一下:

  • 加载图像可以使用BitmapFactory和Bitmap的相关方法。

  • Options参数很牛逼,要多多运用

  • 如果要裁剪或者缩放图片,则只能使用Bitmap的Create系列函数

  • 一定要注意,在加载或者创建Bitmap时,必须要使用try…catch语句捕捉OutOfMemoryError,防止出现OOM。

5、常用函数

(1)copy(Config config,boolean isMutable)

这个函数是根据源图像来创建一个副本,但可以指定副本的像素存储格式。它的两个参数含义为:

config表示存储格式

isMutable表示新创建的Bitmap是否可以更改其中元素。

诶~原来图像的像素还是可以被改变的吗?

其实是的,但是之前所学的加载和创建图片的方法不是每种弄出来的图像的像素都是可以改变的。

我们可以通过下面函数来判断图像像素是否可以更改。true表示可以,false表示不可以。

boolean isMutable();

如果图像的该函数返回了false,你还要用setPixel()等函数来更改,诶,就会报错。

通过BitmapFactory创建出来的Bitmap都是像素不可以更改的,只有通过Bitmap中的下面几个函数创建的Bitmap才是像素可更改的。

copy(Bitmap.Config config,boolean isMutable)

createBitmap(int width,int height,Bitmap.Config config)

createSCaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)

createBitmap(DisplayMetrics display,int width,int height,Bitmap.Config config)

大家谨记,对于像素不可以更改的图像,是不能作为画布的,比如下面这个:

Bitmap bmp = BitmapFactory.decodeResources(getRescoures(),R.drawable.xxx);

Canvas canvas = new Canvas(bmp);

canvas.drawColor(Color.RED);

这个时候,因为Bitmap是由工厂创建出来的,所以像素不可以更改,所以这个时候不能作为画布,这样写就会报错。

(2)extractAlpha()

这个函数的作用是Bitmap中抽出Alpha值,生成一幅只含有Alpha值的图像。像素存储的格式是ALPHA_8,有两个构造函数。

分别是:

Bitmap extractAlpha()

Bitmap extractAlpha(Paint paint,int[] offsetXY)

第二个参数中的Paint是具有MaskFilter效果的Paint对象,一般使用BlurMaskFilter模糊效果。

第二个参数为BlurMaskFilter效果的偏移量。但其取值并不一定与最终BlurMaskFilter的模糊半径一致。它只是一个建议值。

(3)分配控件获取

获取Bitmap的分配空间有三个函数。

//API 19引入,获取Bitmap所分配的内存,API 19以上的机器都使用该函数获取

int getAllocationByteCount();

//获取Bitmap分配的内存,在API 12中引入,在12< API <19 则使用该函数

int getByteCount()

//获取每行所分配的内存大小。Bitmap所占内存 = getRowBytes() * bitmap.getHeight() 即所占内存等于每行所占内存乘以行数。

//这个在API 1中引入,所以API 12一下必须用这个函数

int getRowBytes()

所以一般情况下获取内存分配都是:

if(api > kitkat){

return getAllocationByteCount();

}

if(api > HONEYCOMB_MR1){

return getByteCount();

}

return getRowBytes() * bitmap.getHeight();

(4)recycle()、isRecycled()

这两个与图片回收有关的函数

//强制回收Bitmap所占的内存

public void recycle()

//判断当前Bitmap的内存是否被回收

public final boolean isRecycled()

所以如果要回收内存,要这样写

if(bmp != null && !bmp.isRecycled()){

bmp.recycle();

bmp = null;

system.gc(); //提醒系统及时回收内存

}

注意一:使用内存已经被回收的Bitmap会引起Crash

注意二:是否应该使用recycle()函数主动回收内存

在API 10以前Bitmap的像素级数据被存放在Native内存空间中。

这些数据与Bitmap本身是隔离的。Bitmap本身被存在Dalvik堆中。我们无法预测在Native内存中的像素级数据何时会被释放。这就意味着程序容易超过它的内存限制并且Crash,而自API 11开始,像素级数据与Bitmap本身一起被存放在Dalvik堆中,可以通过GC自动回收。

所以API 11以前的版本,要手动调用 recycle(),而之后的版本则不用强制调用该函数。

(5)setDensity()、getDensity()

这个和BitmapFactory中的 inDensity一样。表示该Bitmap适合DPI值。

上面函数一个是set一个是get

该函数只影响缩放效果,不影响Bitmap本身的内存

另外在setDensity后 xml中该ImageView必须要设置 scaleType=“center” 宽高都是自适应 才能显示该效果,否则图片就会根据屏幕大小而缩放。

(6)setPixel()、getPixel()

这两个函数用于针对Bitmap中某个位置的像素进行设置和获取

//该函数用于指定位置像素进行颜色设置。

public void setPixel(int x,int y.int color)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-DPV1ZBIW-1715487451576)]

[外链图片转存中…(img-fRQK0x4Z-1715487451577)]

[外链图片转存中…(img-Xyk3gOC8-1715487451579)]

[外链图片转存中…(img-gyPN7oAL-1715487451580)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值