Bitmap绘图,旋转,缩放,平移,优化
(一)绘图
(1)绘图
它的两种方式:
1.通过BitmapFactory来找到Bitmap对象
eg.Bitmap bitmap=BitmapFactory.decodeResource(super.getResrouces,R.drawable.android_mldn)
·具体可以看附件中的两张图片
2.通过BitmapDrawable来找到Bitmap对象
eg.mBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.pic)).getBitmap();
(2)在onDraw()方法中,绘制Bitmap
通过DrawBitmap方法
// 第一个参数表示需要绘制的图像对象
// 第二个参数表示截取图像上的区域
// 第三个参数表示屏幕上显示的区域
// 第四个参数表示画笔对象
canvas.DrawBitmap(mBitmap,new Rect(),new Rect(),mPaint);
(二)旋转,缩放,平移
操作步骤
(1)实例化Matrix
eg.Matrix matrix=new Matrix();
·它有三种操作分为:
translate(平移),rotate(旋转),scale(缩放)和skew(倾斜
除了tranaslate不可以指定中心点,其他的都可以
·操作方式分为:
pre,矩阵前乘,是在之前插入一种变换,也是一种累加式的变换
set,直接设置Matrix的值,每一次设置,都会清空之前所有的变换
post,矩阵后乘,在之后变换,是一种累加式的变换
(2)初始化Matrix的值
/* 重置mMatrix */
eg. matrix.reset();
(3)进行旋转,缩放,平移等操作
·旋转
/* 设置旋转 */
// 第一个参数表示旋转的角度
// 第二个参数表示旋转中心的x坐标
// 第三个参数表好旋转中心的y坐标
eg. matrix.setRotate(30,150,20);
·平移
/* 设置平移 */
// 第一个参数表示移动到目标位置的x轴坐标
// 第二个参数表示移动到目标位置的y轴坐标
eg.matrix.setTranslate(10,10)
·缩放
/* 设置缩放 */
// 第一个参数表示移动到目标位置的x轴坐标
// 第二个参数表示移动到目标位置的y轴坐标
eg.matrix.postScale(Scale, Scale, 300, 300);
·倾斜
/* 设置倾斜 */
// 第一个参数表示倾斜的x轴的角度
// 第二个参数表示倾斜的y轴的角度
// 第三个参数表示倾斜的中心的x轴的坐标
// 第四个参数表示倾斜的中心的y轴的坐标
eg.smatrix.postSkew(0.2f, 0.2f, 100, 100);
(4)创建变换后的bitmap
/* 按mMatrix得旋转构建新的Bitmap */
// 第一个参数表示需要显示的图片对象(创建了一个用于显示图像的白纸,该参数表示白纸上要显示的内容)
// 第二个表示图像在白纸(这里的mBitmap2)上的左上角顶点x坐标
// 第三个表示图像在白纸(这里的mBitmap2)上的左上角顶点y坐标
// 第四个参数和第五个参数表示要创建的Bitmap对象的宽度和高度(白纸的宽高)
// 第五个参数表示Bitmap的矩阵状态(这张白纸的显示效果:平移、缩放、倾斜、旋转等状态)
// 第六个参数表示是否设置过滤
·Bitmap mBitmap2 = Bitmap.createBitmap(mBitmap, 0, 0, mBitmapWidth,
mBitmapHeight, mMatrix, true);
(5)绘制变换后的bitmap,
·canvas.drawBitmap(mBitmap2, 0, 0, null);
(6)第四,五步可以采用下面的方式替换
·canvas.drawBitmap(mBitmap, mMatrix, null);
即不用单独建立一个bitmap,直接显示
(三)bitmap的优化
前言:
android应用中,最耗内存的就是图片资源。android系统中,读取位图Bitmap时,分给虚拟机中的图片的堆栈大小事8M,如果超出了就回发生outofMemory异常。所以要对bitmap进行优化
(1)及时回收Bitmap的内存
Bitmap的构造方法是私有的,是通过BitmapFactory类的各种静态方法实例化一个Bitmap。而生成Bitmap对象最终都是通过JNI调用方式是实现的。所以,加载Bitmap到内存里以后,包括两部分内存区域。简单的说,一部分是java部分,一部分是c部分
,这个Btiamap是java部分分配的,不用的时候系统会自动回收、但是那个对应的C可用的内存区域,虚拟机是不能直接调用的,需要调用底层的功能来释放。所以需要用recyle()方法来释放c部分的内存,这个recycle()方法里也的确是调用JNI方法。那么不调用recyle()内存是否会泄露呢?也不是的,android的进程如果被杀掉,那么内存就回释放,也包括c的内存部分
android中,系统进程分为几个级别:系统不足的情况下,杀掉一些低优先级的进程,以提供给其他进程充足的内存空间。在实际开发项目中,有两种对退出程序的方式Process.killProcess(Process.myPid())杀死进程,还有就是用Activity.finish的方式关闭Activity
一般情况下,我们可以在Activity的onStop()或者onDestroy()进行回收
// 先判断是否已经回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();//回收占用的内存
bitmap = null;//置空Bitmap方法
}
System.gc();//调用系统回收器,不能立即回收,可以加快回收的的到来
(2)捕获异常
前言:
通常在实例化Bitmap的时候,会对Bitmap进行OutofMemoryError的异常进行捕获
Bitmap bitmap = null;
try {
// 实例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) { //注意此处是error,不是Exception
//
}
if (bitmap == null) {
// 如果实例化失败 返回默认的Bitmap对象
return defaultBitmapMap;
}
(3)缓存通用的Bitmap对象
·如果是同一张图片,就没有必要多次实例化,以节省内存(可以认为是缓存了Bitmap的对象)
android的缓存技术分为:
1.硬盘缓存
比如,开发网络应用的过程中,可以将一些从网络上获取的数据保存到SD卡中,下次直接从SD卡读取,节省网络流量
2.内存缓存
比如应用程序需要使用到同一对象,也可以放到内存中缓存起来。
(4)压缩图片
eg.
public class BitmapUtils {
public Bitmap optimizeBitmap(String filePath, int maxWidth, int maxHeight) {
Bitmap result = null;
// 创建一个Options图像配置对象
BitmapFactory.Options options = new BitmapFactory.Options();
// inJustDecodeBounds解码图像的模式;如果为true表示先获取图像原始宽高
options.inJustDecodeBounds = true;
// inJustDecodeBounds=true的模式下解码出来的图像为空,值得到图像的宽高
result = BitmapFactory.decodeFile(filePath, options);
int widthRatio = (int) Math.ceil(options.outWidth / maxWidth);
int heightRatio = (int) Math.ceil(options.outHeight / maxHeight);
if (widthRatio > 1 || heightRatio > 1) {
if (widthRatio > heightRatio) {
// inSampleSize主要是配置图像获取的比例,最终显示的比例为1/inSampleSize
// ,注意inSampleSize必须大于1
options.inSampleSize = widthRatio;
} else {
options.inSampleSize = heightRatio;
}
}
// inJustDecodeBounds=false的模式下解码出来的图像才是正常可以显示的图像
options.inJustDecodeBounds = false;
result = BitmapFactory.decodeFile(filePath, options);
return result;
}
/**
* 将Bitmap对象存到sdcard中
* @param bitmap
* @param saveFileName
*/
public static void saveBitmap(Bitmap bitmap, String saveFileName) {
try {
FileOutputStream out = new FileOutputStream(saveFileName);
bitmap.compress(CompressFormat.JPEG, 80, out);
out.flush();
out.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
总结:
如果程序的图片的来源都是程序包中的资源,或者是自己服务器上的图片,图片的大小是开发者可以调整的,那么一般来说,就只需要注意使用的图片不要过大,并且注意代码的质量,及时回收Bitmap对象,就能避免OutOfMemory异常的发生。
如果程序的图片来自外界,这个时候就特别需要注意OutOfMemory的发生。一个是如果载入的图片比较大,就需要先缩小;另一个是一定要捕获异常,避免程序Crash。