最近比较闲,开始整理下接触到的东西吧,加深一下对一些知识点的认识,一直对Android中的Bitmap和Drawable认识比较模糊,用起来也是迷迷糊糊的,所以首先对这两个类做一些分析吧(一般我们可以在官网上查看相关api或是直接在AS里面点进去查看类的源码)
1. Bitmap 与 Drawable区别
首先看看Drawable类的注释
> A Drawable is a general abstraction for "something that can be drawn."
> Most often you will deal with Drawable as the type of resource retrieved for
> drawing things to the screen; the Drawable class provides a generic API for
> dealing with an underlying visual resource that may take a variety of forms.
> Unlike a {@link android.view.View}, a Drawable does not have any facility to
> receive events or otherwise interact with the user.
Drawable是一个抽象类,代表的是“可绘制的东西”的一般抽象。通常你会将Drawable作为绘制事物到屏幕上的资源类型来处理,它提供了一个通用API来处理可能采用多种形式的底层视觉资源。与View不同的是,Drawable没有任何接收事件或以其他方式与用户交互的功能。平常我们使用的时候都是res目录下创建相应的drawable资源,比如button的背景图片,背景颜色,以及button不同的状态的动画效果等。
然后接着看下面一段注释
Though usually not visible to the application, Drawables may take a variety of forms:
> Bitmap: the simplest Drawable, a PNG or JPEG image.
> Nine Patch: an extension to the PNG format allows it to specify information about how to stretch it and place things inside of it.
> Vector: a drawable defined in an XML file as a set of points, lines, and curves along with its associated color information. This type of drawable can be scaled without loss of display quality.
> Shape: contains simple drawing commands instead of a raw bitmap, allowing it to resize better in some cases.
> Layers: a compound drawable, which draws multiple underlying drawables on top of each other.
> States: a compound drawable that selects one of a set of drawables based on its state.
> Levels: a compound drawable that selects one of a set of drawables based on its level.
> Scale: a compound drawable with a single child drawable, whose overall size is modified based on the current level.
这段话说的意思是,虽然Drawable通常对于应用来说是不可见的,但是它可以采取多种形式。如Bitmap, Shape, Layers等。 看到这里还以为Bitmap是继承自Drawable但是并不是,我们来看看Bitmap这个类:
```
public final class Bitmap implements Parcelable {
...
}
```
这个类是继承的Parcelable,它存储的是图片相关的信息。这里另外说一个类BitmapDrawable,它是直接继承自Drawable。通过这个类可以实现Drawable和Bitmap之间的相互转换。
```
//bitmap转drawable
Drawable drawable = new BitmapDrawable(bmp);
//drawable转bitmap
Drawable drawable = gerResource().getDrawable(R.drawable.ic_launcher);
BitmapDrawable bpDrawable = (BitmapDrawable) drawable;
Bitmap bm = bpDrawable.getBitmap();
```
Bitmap代表的是一种视觉资源,侧重于一种数据类型;而Drawble则是提供了一些API来处理这些资源,侧重于行为。
2. Drawable分类与相关API
前面提到过,Drawble通常对应用是不可见的,但是它可以采取多种形式,我的理解是对每种不同的资源都有一个对应的drawable的实现去处理.例如BitmapDrawable, ShapeDrawable, LayerDrawable等
可以看一下Android中Drawable的继承结构,如下图所示:
具体每个类什么意思就不一一说了,好多我也没用过。只说一下Drawable类本身的一些API代表着什么意思:
- setBounds()
这个方法传入一个Rect,为Drawable指定一个边界矩形。这个Rect就是drawable在调用draw()方时绘制的地方。
- getIntrinsicWidth()
返回drawable的固有宽度,包括固有的padding值。一般像一张图片所形成的drawable会返回图片的宽,而像颜色之类的drawable会返回-1。getIntrinsicHeight同理。
- createFromXmlInner()
这个方法就是从xml文件中创建一个Drawable对象。还有一系列的createFromxxx方法。这个createFromXmlInner方法会调用到DrawableInflater类里的inflateFromTag方法,这个方法就会根据我们在xml中的根标签创建不同的drawable对象。例如:根标签是“selector”,就会创建StateListDrawable对象,根标签是“animated-selector”,就会创建AnimatedStateListDrawable对象。
- inflate()
从xml资源文件中解析属性,每个drawable对象只能调用一次,一般Framework在从xml资源中创建Drawable实例的时候已经调用过了。
- draw()
把之前设置了Bounds, alpha值等的drawable画到canvas上。如果要自定义Drawable通过需要重写draw()方法
3. Bitmap相关配置与加载
Bitmap
1. Bitmap.CompressFormat 与 Bitmap.Config
CompressFormat与Config是Bitmap的内部枚举类。其中CompressFormat指定位图可以压缩到的已知格式,而Config代表可能的位图配置。位图配置描述像素如何存储。 这影响质量(颜色深度)以及显示透明/半透明颜色的能力。
CompressFormat的枚举值有JPEG, PNG, WEBP三种,都是比较常见的。而Config的枚举值有ALPHA_8, ARGB_4444, ARGB_8888, RGBA_F16, RGB_565, HARDWARE
其中的字母意义是 A:透明度 R:红色 G:绿 B:蓝
ALPHA_8:每个像素被存储在单一通道里,占一个字节,只有透明度,没有颜色。
ARGB_4444:每个像素被存储在2个字节里,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位,由于这个配置下的图片质量太差已经被废弃
ARGB_8888:每个像素被存储在4个字节里,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位
RGBA_F16:每个像素被存储在8个字节里。
RGB_565:每个像素被存储在2个字节里,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位
HARDWARE:这是一种特殊的配置,bitmap被存储在graphic memory里使用这个配置。
一般来说,每个像素占用的空间越大,图片的品质就越高,但是这样相应占用的内存也就越大。如果我们对于图片的要求不是那么高的话可以选择使用RGB_565。
2. Bitmap的常见API
- getWidth()
获取bitmap的宽度,getHeight()用来获取高度
- getRowBytes()
获取Bitmap每一行所占用的空间数,可以用getRowBytes()*getHeight来获取bitmap大小,文档上说不应该用这个方法来获取bitmap大小,而是使用getAllocationByteCount()
- getByteCount()
获取可以存储此bitmap像素的最小字节数,看源码,这个方法内部调用了getRowBytes() * getHeight()。同样获取大小也不应该使用该方法
- getAllocationByteCount()
获取用来存储bitmap像素所分配的内存大小。如果这个bitmap被重用来解码更小尺寸的其它位图,这个值可能会比getByteCount获取的值大一些。在bitmap的生命周期内此值不会改变。
- isMutable()
返回bitmap是否易变的,即bitmap是否可以被修改或写入,例如在调用setConfig时,我会先调用该方法,如果返回false,则调用setConfig时会出错。
- setConfig()
给图片设置位图配置,设置的值就是前面介绍的Config的枚举值,之后bitmap就会按照配置值存储。一般默认是ARGB_8888, 如果对图片质量要求不高的话,设置成RGB_565后可以起到压缩图片的作用。
- createBitmap
从源bitmap的子集中返回一个不可变的bitmap。新的bitmap可能与源bitmap相同,也可能是源bitmap的一个副本。这个方法可以通过缩放源bitmap的宽高,来对源bitmap进行压缩。
- createScaledBitmap
通过对已存在的bitmap缩放来创建一个新的bitmap。需要注意的是,如果指定的宽高和原始bitmap的宽高一致的话,就会返回原始bitmap。我之前出的一个Bug就是,调用这个方法之后以为获取到的永远是新的bitmap,就把原始bitmap回收掉了,从而产生了空指针。这个方法和createBitmap其实原理都是一样的,它的内部就调用了createBitmap。可以通过缩放图片的宽高来达到压缩图片的效果,用来做缩放压缩。
- compress()
将bitmap按照指定的格式和质量参数压缩到输出流中,可以用来做质量压缩。需要注意的是,这个方法压缩的是存储的大小,而不是bitmap的内存大小,内存大小是不变的。我的理解是这个方法主要用于上传下载或保存时,减小文件体积,优化上传下载速度。如果想减小bitmap的内存占用,还是需要从像素字节,长和宽这三个因素来调整。另外PNG格式是无损的,不能用这个方法进行压缩。
- isRecycled()
判断bitmap是否被回收掉了,很多方法调用之前都要先调用这个方法判断一下,防止发生异常
3. BitmapFactory
Android中Bitmap的加载离不开BitmapFactory类,BitmapFactory类是Android提供的一个工具类,用于从不同的数据来源中解析创建Bitmap。数据来源包括,Resource, 文件,流,字节数组。主要方法如下:
- decodeFile()
- decodeResource()
- decodeStream()
- decodeByteArray()
BitmapFactory中有一个非常重要的内部类Options,这个类就是用来配置加载的图片的相关信息的,Options中有几个很重要的值:
- public Bitmap inBitmap
设置Bitmap是否被重用,如果这个值被设置了,decode方法会在加载内容的时候去reuse已经存在的bitmap. 重用bitmap以便提升性能。
- public boolean inJustDecodeBounds
将该值设置为true时,我们并不会返回真正的bitmap,而是将bitmap的相关信息,如宽,高等放入options。这样你就可以根据原始图片的宽高和你需要的宽高来计算inSampleSize。然后再将inJustDecodeBounds的值设置为false, 来真正加载图片。
- public int inSampleSize
这个代表采样率。一般是大于1的数,例如这个值如果为2的话,那么采样后的图片宽高为原始图片的1/2。按照图片内存大小计算方法,内存就为原始图片的1/4。inSampleSize值小于1时会被当成1对待。
- public int inDensity
设置位图的像素密度,即每英寸有多少个像素
上面一系列的decodeXXX方法都支持Options做为参数,我们可以通过设置Options来完成bitmap的加载。
1. Bitmap 与 Drawable区别
首先看看Drawable类的注释
> A Drawable is a general abstraction for "something that can be drawn."
> Most often you will deal with Drawable as the type of resource retrieved for
> drawing things to the screen; the Drawable class provides a generic API for
> dealing with an underlying visual resource that may take a variety of forms.
> Unlike a {@link android.view.View}, a Drawable does not have any facility to
> receive events or otherwise interact with the user.
Drawable是一个抽象类,代表的是“可绘制的东西”的一般抽象。通常你会将Drawable作为绘制事物到屏幕上的资源类型来处理,它提供了一个通用API来处理可能采用多种形式的底层视觉资源。与View不同的是,Drawable没有任何接收事件或以其他方式与用户交互的功能。平常我们使用的时候都是res目录下创建相应的drawable资源,比如button的背景图片,背景颜色,以及button不同的状态的动画效果等。
然后接着看下面一段注释
Though usually not visible to the application, Drawables may take a variety of forms:
> Bitmap: the simplest Drawable, a PNG or JPEG image.
> Nine Patch: an extension to the PNG format allows it to specify information about how to stretch it and place things inside of it.
> Vector: a drawable defined in an XML file as a set of points, lines, and curves along with its associated color information. This type of drawable can be scaled without loss of display quality.
> Shape: contains simple drawing commands instead of a raw bitmap, allowing it to resize better in some cases.
> Layers: a compound drawable, which draws multiple underlying drawables on top of each other.
> States: a compound drawable that selects one of a set of drawables based on its state.
> Levels: a compound drawable that selects one of a set of drawables based on its level.
> Scale: a compound drawable with a single child drawable, whose overall size is modified based on the current level.
这段话说的意思是,虽然Drawable通常对于应用来说是不可见的,但是它可以采取多种形式。如Bitmap, Shape, Layers等。 看到这里还以为Bitmap是继承自Drawable但是并不是,我们来看看Bitmap这个类:
```
public final class Bitmap implements Parcelable {
...
}
```
这个类是继承的Parcelable,它存储的是图片相关的信息。这里另外说一个类BitmapDrawable,它是直接继承自Drawable。通过这个类可以实现Drawable和Bitmap之间的相互转换。
```
//bitmap转drawable
Drawable drawable = new BitmapDrawable(bmp);
//drawable转bitmap
Drawable drawable = gerResource().getDrawable(R.drawable.ic_launcher);
BitmapDrawable bpDrawable = (BitmapDrawable) drawable;
Bitmap bm = bpDrawable.getBitmap();
```
Bitmap代表的是一种视觉资源,侧重于一种数据类型;而Drawble则是提供了一些API来处理这些资源,侧重于行为。
2. Drawable分类与相关API
前面提到过,Drawble通常对应用是不可见的,但是它可以采取多种形式,我的理解是对每种不同的资源都有一个对应的drawable的实现去处理.例如BitmapDrawable, ShapeDrawable, LayerDrawable等
可以看一下Android中Drawable的继承结构,如下图所示:
具体每个类什么意思就不一一说了,好多我也没用过。只说一下Drawable类本身的一些API代表着什么意思:
- setBounds()
这个方法传入一个Rect,为Drawable指定一个边界矩形。这个Rect就是drawable在调用draw()方时绘制的地方。
- getIntrinsicWidth()
返回drawable的固有宽度,包括固有的padding值。一般像一张图片所形成的drawable会返回图片的宽,而像颜色之类的drawable会返回-1。getIntrinsicHeight同理。
- createFromXmlInner()
这个方法就是从xml文件中创建一个Drawable对象。还有一系列的createFromxxx方法。这个createFromXmlInner方法会调用到DrawableInflater类里的inflateFromTag方法,这个方法就会根据我们在xml中的根标签创建不同的drawable对象。例如:根标签是“selector”,就会创建StateListDrawable对象,根标签是“animated-selector”,就会创建AnimatedStateListDrawable对象。
- inflate()
从xml资源文件中解析属性,每个drawable对象只能调用一次,一般Framework在从xml资源中创建Drawable实例的时候已经调用过了。
- draw()
把之前设置了Bounds, alpha值等的drawable画到canvas上。如果要自定义Drawable通过需要重写draw()方法
3. Bitmap相关配置与加载
Bitmap
1. Bitmap.CompressFormat 与 Bitmap.Config
CompressFormat与Config是Bitmap的内部枚举类。其中CompressFormat指定位图可以压缩到的已知格式,而Config代表可能的位图配置。位图配置描述像素如何存储。 这影响质量(颜色深度)以及显示透明/半透明颜色的能力。
CompressFormat的枚举值有JPEG, PNG, WEBP三种,都是比较常见的。而Config的枚举值有ALPHA_8, ARGB_4444, ARGB_8888, RGBA_F16, RGB_565, HARDWARE
其中的字母意义是 A:透明度 R:红色 G:绿 B:蓝
ALPHA_8:每个像素被存储在单一通道里,占一个字节,只有透明度,没有颜色。
ARGB_4444:每个像素被存储在2个字节里,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位,由于这个配置下的图片质量太差已经被废弃
ARGB_8888:每个像素被存储在4个字节里,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位
RGBA_F16:每个像素被存储在8个字节里。
RGB_565:每个像素被存储在2个字节里,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位
HARDWARE:这是一种特殊的配置,bitmap被存储在graphic memory里使用这个配置。
一般来说,每个像素占用的空间越大,图片的品质就越高,但是这样相应占用的内存也就越大。如果我们对于图片的要求不是那么高的话可以选择使用RGB_565。
2. Bitmap的常见API
- getWidth()
获取bitmap的宽度,getHeight()用来获取高度
- getRowBytes()
获取Bitmap每一行所占用的空间数,可以用getRowBytes()*getHeight来获取bitmap大小,文档上说不应该用这个方法来获取bitmap大小,而是使用getAllocationByteCount()
- getByteCount()
获取可以存储此bitmap像素的最小字节数,看源码,这个方法内部调用了getRowBytes() * getHeight()。同样获取大小也不应该使用该方法
- getAllocationByteCount()
获取用来存储bitmap像素所分配的内存大小。如果这个bitmap被重用来解码更小尺寸的其它位图,这个值可能会比getByteCount获取的值大一些。在bitmap的生命周期内此值不会改变。
- isMutable()
返回bitmap是否易变的,即bitmap是否可以被修改或写入,例如在调用setConfig时,我会先调用该方法,如果返回false,则调用setConfig时会出错。
- setConfig()
给图片设置位图配置,设置的值就是前面介绍的Config的枚举值,之后bitmap就会按照配置值存储。一般默认是ARGB_8888, 如果对图片质量要求不高的话,设置成RGB_565后可以起到压缩图片的作用。
- createBitmap
从源bitmap的子集中返回一个不可变的bitmap。新的bitmap可能与源bitmap相同,也可能是源bitmap的一个副本。这个方法可以通过缩放源bitmap的宽高,来对源bitmap进行压缩。
- createScaledBitmap
通过对已存在的bitmap缩放来创建一个新的bitmap。需要注意的是,如果指定的宽高和原始bitmap的宽高一致的话,就会返回原始bitmap。我之前出的一个Bug就是,调用这个方法之后以为获取到的永远是新的bitmap,就把原始bitmap回收掉了,从而产生了空指针。这个方法和createBitmap其实原理都是一样的,它的内部就调用了createBitmap。可以通过缩放图片的宽高来达到压缩图片的效果,用来做缩放压缩。
- compress()
将bitmap按照指定的格式和质量参数压缩到输出流中,可以用来做质量压缩。需要注意的是,这个方法压缩的是存储的大小,而不是bitmap的内存大小,内存大小是不变的。我的理解是这个方法主要用于上传下载或保存时,减小文件体积,优化上传下载速度。如果想减小bitmap的内存占用,还是需要从像素字节,长和宽这三个因素来调整。另外PNG格式是无损的,不能用这个方法进行压缩。
- isRecycled()
判断bitmap是否被回收掉了,很多方法调用之前都要先调用这个方法判断一下,防止发生异常
3. BitmapFactory
Android中Bitmap的加载离不开BitmapFactory类,BitmapFactory类是Android提供的一个工具类,用于从不同的数据来源中解析创建Bitmap。数据来源包括,Resource, 文件,流,字节数组。主要方法如下:
- decodeFile()
- decodeResource()
- decodeStream()
- decodeByteArray()
BitmapFactory中有一个非常重要的内部类Options,这个类就是用来配置加载的图片的相关信息的,Options中有几个很重要的值:
- public Bitmap inBitmap
设置Bitmap是否被重用,如果这个值被设置了,decode方法会在加载内容的时候去reuse已经存在的bitmap. 重用bitmap以便提升性能。
- public boolean inJustDecodeBounds
将该值设置为true时,我们并不会返回真正的bitmap,而是将bitmap的相关信息,如宽,高等放入options。这样你就可以根据原始图片的宽高和你需要的宽高来计算inSampleSize。然后再将inJustDecodeBounds的值设置为false, 来真正加载图片。
- public int inSampleSize
这个代表采样率。一般是大于1的数,例如这个值如果为2的话,那么采样后的图片宽高为原始图片的1/2。按照图片内存大小计算方法,内存就为原始图片的1/4。inSampleSize值小于1时会被当成1对待。
- public int inDensity
设置位图的像素密度,即每英寸有多少个像素
上面一系列的decodeXXX方法都支持Options做为参数,我们可以通过设置Options来完成bitmap的加载。