Bitmap 在绘图中主要有两种:
1).转换为BitmapDrawable对象使用
Bitmap bmp = BitmapFactory.decodeResource(res,R.drawable.icon);
BitmapDrawable bmpDraw = new BitMapDrawable(bmp);
iv.setImageDrawable(bmpDraw);
2)当作画布使用
1.默认画布 onDraw(Canvas canvas) 2.自建画布 new Canvas(bitmap)
一张位图所占内存 = 图片长度(px) * 图片宽度(px) * 一个像素点所占用的字节。
| 类型 | ||
|---|---|---|
| ALPHA_8 | 8 位 Alpha 位图 (没有颜色,只有透明度) | 一个像素占1字节 |
| ARGB_4444 | 16位ARGB位图 | 一个像素 16位 2字节 |
| ARGB_8888 | 32位ARGB位图 | 一个像素 32位 4字节 |
| RGB_565 | 16位RGB位图(没有透明度) | 一个像素 16位 2字节 |
//一般建议使用ARGB_8888格式来存储Bitmap,如果没有透明度要求,可以使用RGB_565节省内存开销。
内存中存储Bitmap对象大小: Bitmap的宽Bitmap的高每个像素所占大小。
文件中的Bitmap大小: 根据Jpg,png 格式来压缩。
创建Bitmap方法: 一、BitmapFactory
1.decodeResource(Resources res, int id) //用来从资源文件中解码一张位图
2.decodeFile(String pathName) //通过文件路径来加载图片。pathName是全路径名。
3.decodeByteArray(byte[] data, int offset, int length) //根据byte数组来解析Bitmap,data压缩图像数据的字节数组,offset 图像数据偏移量,length字节数。
一般使用步骤:
- 开启异步去请求图片
- 网络返回inputStream
- 把inputStream 转 byte[]
- 解析 Btimap bm = BitmapFactory.decodeByteArray(byte,0,byte.length)
4.decodeFileDescriptor
public static Bitmap decodeFileDescriptor(FileDescriptor fd)
public static Bitmap decodeFileDescriptor(FileDescriptor fd,Rect outPadding,Options opts)
//fd:包含解码位图数据的文件路径。outPadding 用于返回矩形的内边距,没有返回成功返回(-1,-1,-1,-1)
decodeFileDescriptor相比decodeFile会更节省内存,前者直接调用Native层函数,由Native层解析出Bitmap返回。后者在调用nativeDecodeStream前,可能会申请两次空间。
5.decodeStream
public static Bitmap decodeStream(InputStream is)
public static Bitmap decodeStream(inputStream is,Rect outPadding,Options)
//这个方法也可以用来加载网络图片
BitmapFactory.Options
1.inJustDecodeBounds 获取图片信息, true 表示只解析图片信息,不获取图片,不分配内存
BitmapFactory.Options op = new BitmapFactory.Options()
op.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.DecodeRecourse(getRecourse(),R.drawable.icon,op);
//宽 options.outWidth,高 options.outHeight,MiME类型 options.outMimeType
2.inSampleSize 压缩图片.采样率,每多少像素作为一个结果返回,其余的被抛弃。官方建议取2的幂数。设置时应注意使得缩放后的图片尺寸尽量大于等于相应的ImageView大小(拿原始宽高和目标宽高相比,取宽比,高比两个比值中最小的)
屏幕上1英寸对应的px数 = 每英寸对应的dpi数 * dpi对应的px数
加载资源文件中的图片。动态缩放图片:缩放比例 = 屏幕分辨率/文件所在文件夹的分辨率
加载SD文件中的图片:不进行缩放。
3.inScaled 缩放 false表示不缩放 默认为true
inDensity 设置文件所在资源文件夹的屏幕分辨率
inTargetDesity 真实显示的屏幕分辨率
scale = inTargetDensity / inDensity
4.inPreferredConfig 设置像素存储格式
二、Bitmap静态创建法
createBitmap 方法
在加载或创建Bitmap时,要放在try catch中捕捉异常,防止oom
通过BitmapFactoory加载的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)
//在 API 17 中引入
createBitmap(DisplayMetrics display, int width, int height, Bitmap.Config config)
可以通过isMutabel()来判断像素是否可以更改。如果不能更改,setPixel()会报错。
注意:只有上面几个方法是像素可更改的,像素不能更改的图像,是不能作为画布的(new Canvas(Bitmap))
extractAlpha() 从Bitmap中抽取出alpha值,返回只有alpha值得图像,存储格式alpha_8
extractAlpha(Paint paint,int[] offsetXY) // paint具有MaskFilter效果得paint对象,一般用BLurMaskFilter模糊效果。offsetXY 返回添加BlurMaskFilter效果后远点得偏移量。在绘制得时候需要把这个偏移量考虑进去,比如6px得模糊效果 ,那么绘制时候不能从(0,0)开始,要从(-6,-6)开始计算,所以offsetXY值为[-6,-6],那么源图像绘制就需要canvas.drawBitmap(srcBtimap,-offsetXY[0],-offsetXY[1],null)
onFinishInflate()函数调用时机是系统将XML解析出对应得控件实例得时候。
Bitmap获取分配空间
getAllocationByteCount() // API>=19以上需要使用这个函数。
getByteCount() // api>=12 && api< 19 用这个函数
getRowBytes() // api<12 用这个函数 内存 = getRowBytes() * bitmap.getHeight()
-
recycle() 、isRecycled()//使用已被recycle得bitmap会crash.
-
setDensity()、getDensity() // 对应inDensity。
-
setPixel(int x,int y,int color)、getPixel() //针对Bitmap中某个位置的像素进行设置和获取。
-boolean compress(CompressFormat format, int quality, OutputStream stream) //压缩图像,将压缩的图像写入输出流。format 压缩格式。quality质量 0-100, png等无损格式,忽略此项。stream 输出流.返回值表示成功失败
其他
1.添加水印:
public Bitmap createWaterBitmap(Bitmap srcBitmap,Bitmap waterBitmap){
if(null = srcBitmap){
return null;
}
int w = srcBitmap.getWidth();
int h = srcBitmap.getHeight();
int ww = waterBitmap.getWidth();
int wh = waterBitmap.getHeight();
Bitmap newB = Bitmap.createBitmap(w,h,Bitmap.config.RARGB_8888);
Canvas canvas = new Canvas(newB);
canvas.drawBitmap(src,0,0,null);
canvas.drawBitmap(waterBitmap,w-ww,h-wh,null);
return newB;
}
2.Bitmap设置ANTI_ANLIAS_FLAG无效。
onDraw()中是有效的,因为会先清空canvas,再绘制所有内容.
new Canvas()中效果就打折扣。
ANTI_ANLIAS_FlAG 原理是通过混合前景色和背景色来来产生过渡边缘的。
解决思路:
-
避免重绘 避免在onDraw(),onMeasure(),onLayout()中多次绘制
-
在绘制前清空
canvas.drawColor(Color.TRANSPARENT,PosterDuff.Mode.CLEAR);