这里就不贴图了,最终效果与采用Xfermode叠加方案是一样,并且不需要创建新的Bitmap,不会导致内存占用率大量提高。但是这种方案也有其局限性,由于我们只能通过Path来限制其在画布可绘画的区域,并且Path只支持一些几何形的图案,所以预览框形状被限死在几何形图案集合内。
这里总结一下上面两种方案和其应用场景:
如果是几何形的预览框,那么首推限制绘画区域的方案,内存占用率低。
如果是非几何形的预览框(例如卡通形状的预览框),那么在这里给出的方案里,你只能通过Xfermode方式来实现了,不过使用这种方式需要注意内存的占有率。
图片缩放&移动实现#####
这里的图片缩放、移动全部通过Matrix实现的。其实移动的实现方式可以采用两种方式:
View.scrollBy(int,int)或者View.scrollTo(int,int)方式实现.
图片Matrix处理。
但是这里由于需要实现缩放功能,所以干脆统一采用Matrix方式来实现。Matrix是一组参数集合,其中不同的参数对应着不同的功能处理(平移/缩放等),具体可以查看博文Qiengo-Android Matrix
这里只讲述Matrix实现的缩放与移动,不对View.scroll**移动方式多做说明。
在通过Matrix实现缩放、移动之前,需要调用ImageView.setScaleType(ImageView.ScaleType.MATRIX),将ImageView的缩放方式设置为MATRIX。
Matrix移动实现
Matrix移动的实现十分简单,通过记录最后移动点与当前移动点的距离就可以实现移动功能。
//motionX,motionY为当前触摸的坐标
public void drag(float motionX, float motionY) {
//mLastY,mLastX 为上一次触摸的坐标
float moveX = motionX -mLastX;
float moveY = motionY - mLastY;
//通过postTranslate方法就可以移动到相应的位置
mView.getImageMatrix().postTranslate(moveX,moveY);
//重画视图
mView.invalidate();
}
Matrix缩放实现
Martix缩放功能也是相对简单。通过ScaleGestureDetector方式实现了缩放功能。我们主要通过实现ScaleGestureDetector,重写onScale方法,当然由于与移动功能叠加,所以需要在缩放的时候,屏蔽掉移动功能,所以我们需要记录缩放开始与结束。
@Override
public boolean onScale(ScaleGestureDetector detector) {
//px,py为缩放的中心点,以该点为中心点进行缩放
float px = detector.getFocusX();
float py = detector.getFocusY();
//缩放的比例 大于1为放大, 小于1为缩小
float scaleFactor= detector.getScaleFactor();
//通过postScale方式来实现缩放效果
mView.getImageMatrix().postScale(scaleFactor,scaleFactor,
px,py);
//重画视图
mView.invalidate();
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
//设置缩放标志位
isScale = true;
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
isScale = false;
}
@Override
public boolean touch(MotionEvent event){
//缩放手势处理
mGestureDetector.onTouchEvent(event);
//如果不在缩放中,则处理普通的触摸事件
if(!isScale){
}
}
return false;
}
裁剪功能实现#####
如果没有移动与缩放功能,那么裁剪会是一个相当简单的功能,因为其裁剪的位置总是固定的,但是如果加入了移动与缩放,那么事情就变的复杂了。当移动后与缩放后,裁剪的位置与大小都发生了变化,另外,移动和缩放可能导致图片部分或者全部不在预览框内,这些情况我们都需要进行处理,下面我们看看怎么正确的裁剪出预览框显示的图片。由于为了照顾到图片不在预览框的情况,所以我们采用了以下方式来做最终的图片裁剪:
public void crop() {
//其中width与height是最终实际裁剪的图片大小,saveBitmap就是最终裁剪的图片
Bitmap saveBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(saveBitmap);
//bitmap为原图,这里就是最终裁剪图片的实现方式,其中cropRect是裁剪区域,showRect是最终显示在画布的区域
canvas.drawBitmap(bitmap, cropRect, showRect, new Paint());
}
从上面代码段,我们可以清晰的知道影响裁剪的因素有:
最终裁剪的图片大小
实际裁剪的四个角的位置(相对于原图)
显示裁剪图片的四个角的位置(相对于画布)
注意以下的计算的前置条件是原图片中心点与预览框中心点均与屏幕中心点重叠
其中裁剪的图片大小我们很容易就可以根据裁剪预览框的大小与原图片缩放的倍数来获取。
我们通过裁剪的左上角起始坐标与最终裁剪的图片大小,来获取裁剪的四个位置。
//actuallyWidth与actuallyHeight为裁剪的实际长宽
//原图中心点x坐标--实际图片x坐标中心点-横坐标的实际偏移量,就可以得出裁剪的左上角位置
//,由于这里采用比较心点的方式去得到实际横坐标偏移量,所以这里可以不用理会缩放与移动产生的偏移量。
cropLeft = (int) (bitmap.getWidth()/2-mImageView.getActuallyScrollX()/scale-actuallyWidth/2);
//这里也是一样
cropTop = (int) (bitmap.getHeight()/2-mImageView.getActuallyScrollY()/scale - actuallyHeight/2);
//这里会进行边界判断,默认右边点为左边点+宽度
int cropRight = cropLeft+actuallyWidth;
int cropBottom = cropTop+actuallyHeight;
//裁剪总宽度超出原图宽度,需要重新设置右边点位置为图片宽度
if(cropRight>bitmap.getWidth()){
cropRight = bitmap.getWidth();
}
//裁剪总高度超出原图高度,需要重新设置右边点位置为图片高度
if(cropBottom>bitmap.getHeight()){
cropBottom = bitmap.getHeight();
}