从4月初到5月份 ,差不多一个多月,终于把裁剪图片的功能码出来了,期间,解决了一个又来一个问题,好吧,问题总是会有的。
这里大致介绍这个裁剪功能技术点、主要难点,实现原理。
图片缩放、移动
裁剪区域预览
裁剪(包括越图片边界裁剪)
边界限制
主要难点裁剪区域预览
裁剪
边界限制
实现原理#### 裁剪预览区域的实现#####在我做过的项目中,就有使用过一些网络上开源的裁剪功能:半透明遮罩层的矩形预览框功能。它的实现原理是在裁剪预览区域外的地方填充了几个半透明的矩形框,进而实现了矩形裁剪预览框功能,如下图。
这种功能虽然可以实现预览功能,但是仅仅局限于当预览区外的地方可以通过规则的形状填充,如果是圆形的裁剪预览框,那么就没办法通过这种方式来实现了。
所以我们需要另外想过办法来实现圆形的预览框。在一开始的时候,我这边的思路是通过在半透明的遮罩层上镂空一个预览框。我们来试试在半透明的遮罩层上叠加一个透明的预览框。 public void onDraw(Canvas canvas){ //绘画半透明遮罩 canvas.drawColor(Color.parseColor("#90000000")); Paint paint = new Paint(); paint.setColor(Color.TRANSPARENT); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); int left = getWidth()/2; int top = getHeight()/2; //绘制透明预览框 canvas.drawCircle(left, top, 300, paint); }
效果可见下图。
可以看出,虽然在中间白色的预览框是全透明的一个裁剪预览框,,下面还是会有一层半透明的遮罩层覆盖住图片,实现不了预览框全透明的效果。
看来这种简单的叠加方式是无法实现我们的需求,所以通过搜寻资料,最终,发现可以采用Xfermode方式来实现。通过设定Xfermode模式,可以将两个重叠的层通过一定的方式来显示,例如下层是半透明遮罩,上层是透明圆形框,那么可以通过设置相应的Xfermode模式来实现。我们改一下上面的代码:
public void onDraw(Canvas canvas){ //这里需要通过bitmap创建canvas才能对Xfermode生效果,具体原因这里也不大清楚 Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444); Canvas xFerCanvas = new Canvas(bitmap); //绘画半透明遮罩 xFerCanvas.drawColor(Color.parseColor("#90000000")); Paint paint = new Paint(); paint.setColor(Color.TRANSPARENT); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); //设置当前画笔的Xfermode模式,不同的模式效果可以参照Google提供的Demo-ApiDemos/Graphics/XferModes paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); int left = getWidth()/2; int top = getHeight()/2; //绘制透明预览框 xFerCanvas.drawCircle(left, top, 300, paint); //最后将生成的bitmap绘制到我们的画布上 canvas.drawBitmap(bitmap,0,0,null); bitmap.recycle(); System.gc(); }效果可见下图
可以看出,实现了我们想要的效果。
对Xfermode更加详细的讲解可以阅读博文时之沙-Android 颜色渲染(九) PorterDuff及Xfermode详解,里面有详细的讲解不同的Xfermode对层叠加的不同效果。虽然这种方案可以实现效果,但是这种方案有一个很大的缺点,就是需要创建一个新的Bitmap,会导致内容占用率大量提高。所以这里通过了博文JianTao_Yang-Android ImageCropper 矩形 圆形 裁剪框找到了第二种方案。第二种方案的实现思路是:在绘画半透明遮罩之前,先将画布可以绘画位置限定在裁剪预览框之外,这样绘画的半透明遮罩自然就空下了中间的预览框,这样就实现了该功能。 public void onDraw(Canvas canvas){ Paint paint = new Paint(); paint.setColor(Color.TRANSPARENT); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); int left = getWidth()/2; int top = getHeight()/2; //创建圆形预览框 Path path = new Path(); path.addCircle(left, top, 300, Path.Direction.CW); //保存当前canvas 状态 canvas.save(); //将当前画布可以绘画区域限制死为预览框外的区域 canvas.clipPath(path, Region.Op.DIFFERENCE); //绘画半透明遮罩 canvas.drawColor(Color.parseColor("#90000000")); //还原画布状态 canvas.restore(); }