最近做的项目中经常会遇到图像的剪切与拉伸,当中也遇到了不少麻烦,现在在此总结下。
1. Bitmap的剪切
通常是用到了Bitmap类的createBitmap方法的几个重载方法:
public static Bitmap createBitmap (Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
该方法结合Matrix(矩阵)来实现Bitmap的剪切,很多人对这个方法的剪切过程比较模糊,经过我的测试,该方法剪切图片其实是分为两个步骤的。第一步先对源图像从(x,y)位置截取宽为width,高为height的图像缓存起来。第二步对这个缓存图像进行matrix变换生成新的Bitmap作为返回值。这里一定要注意,由于第一步的剪切原理,x,y,width,height必须满足 x+width<=source.getWidth(), h+height<=source.getHeight();否则会抛出异常。
public static Bitmap createBitmap (Bitmap source, int x, int y, int width, int height)该方法和上面的方法类似 ,只不过去掉了Matrix变换。本人觉的 若只是单纯的剪切图像还是用这个比较靠谱因为若是把图像剪切和图像的矩阵变换混在一起 往往会得不到想要的效果。
2. Bitmap的拉伸
Bitmap的拉伸最有效的方法就是:
Bitmap mBitmap = Bitmap.createScaledBitmap(bmp, mScreenWidth, mScreenHeight, true);
该方法是以源图像的左上角为中心将源图像拉伸后生成一个宽为 mScreenWidth,高度为mScreenHeight的新图像作为返回值。游戏开发中为了适应不同屏幕尺寸的手机,通常都会将游戏背景按比例拉伸来适配相应的屏幕尺寸,这里就是用到了该方法。需要注意的一点是该方法不是按比例拉伸的,为了拉伸后的图像不变型 ,通常需要事先计算好按比例拉伸好后的宽高。
图片拉伸还有另一种方法:
ImageView imgvLogo = findViewById(R.id.imgv_Logo);
imgvLogo.setScaleType(ImageView.ScaleType.CENTER);
即将图像作为ImageView的背景 ,然后通过设置ImageView的scaleType属性来拉伸图像。这种用法的优点是不用创建新的对象节省内存,缺点是灵活性差。这里顺带介绍下scaleType用到的几个枚举值:
fitXY : 拉伸图片(不按比例)以填充View的宽高
fitStart : 按比例拉伸图片,拉伸后图片的高度为View的高度,且显示在View的左边 fitCenter :按比例拉伸图片,拉伸后图片的高度为View的高度,且显示在View的中间。
fitEnd :按比例拉伸图片,拉伸后图片的高度为View的高度,且显示在View的右边 center : 按原图大小显示图片,但图片宽高大于View的宽高时,截图图片中间部分 显示
centerCrop: 按比例放大原图直至等于某边View的宽高显示。
centerInside :当原图宽高小于或等于View的宽高时,按原图大小居中显示;反之将 原图缩放至View的宽高居中显示。
3. 细心的同学其实已经注意到了,上述图像的剪切与拉伸都创建了一个新的Bitmap 对象,如果开发中需要对很多个图片做拉伸或者剪切,使用上述方法就会对系统内存造成极大的浪费。那么有什么方法可以避免这种情况呢? 答案就是 在绘图过程中对图像进行剪切或拉伸。
这里的绘图中剪切与拉伸图像本质上是绘制源图像的部分区域而已,用到的方法是:
public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) ;
这个方法是Canvas类的方法, src 是原始图像的部分矩形区域, dst是对原始图像做了剪切或或其他操作(matrix变换)后的图像矩形区域。这里并没有生成新的对象,只是绘制了源图像的部分区域。
还有一种绘制图像的方法用于图像的Matrix变化,同样也不会生成新的对象:
public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint);
这个也是Canvas类的方法,源图像的变化操作都保存在matrix里,绘制图像时会先对源图像进行相应的变化然后在绘制再画布上。关于图像的Matrix变换比较复杂,我会在后续的文章中详细介绍。