效果图:
代码:
```
public class CropImageViewextends AppCompatImageView {
private ContextmContext;
//裁剪框边框画笔
private PaintmBorderPaint;
//裁剪框九宫格画笔
private PaintmGuidelinePaint;
//绘制裁剪边框四个角的画笔
private PaintmCornerPaint;
//判断手指位置是否处于缩放裁剪框位置的范围:如果是当手指移动的时候裁剪框会相应的变化大小
//否则手指移动的时候就是拖动裁剪框使之随着手指移动
private float mScaleRadius;
private float mCornerThickness;
private float mBorderThickness;
//四个角小短边的长度
private float mCornerLength;
//用来表示图片边界的矩形
private RectFmBitmapRect =new RectF();
//手指位置距离裁剪框的偏移量
private PointFmTouchOffset =new PointF();
private CropWindowEdgeSelectormPressedCropWindowEdgeSelector;
private float oldW =0f;
private float oldH =0f;
private RectFodlBitmapRect =new RectF();
public CropImageView(Context context) {
super(context);
init(context);
}
public CropImageView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context);
}
/**
* 里面的值暂时写死,也可以从AttributeSet里面来配置
*
* @param context
*/
private void init(@NonNull Context context) {
mContext = context;
mBorderPaint =new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStrokeWidth(UIUtil.dip2px(context, 3));
mBorderPaint.setColor(Color.parseColor("#AA000000"));
mGuidelinePaint =new Paint(Paint.ANTI_ALIAS_FLAG);
mGuidelinePaint.setStyle(Paint.Style.STROKE);
mGuidelinePaint.setStrokeWidth(UIUtil.dip2px(context, 1));
mGuidelinePaint.setColor(Color.parseColor("#AAFFFFFF"));
mCornerPaint =new Paint(Paint.ANTI_ALIAS_FLAG);
mCornerPaint.setStyle(Paint.Style.STROKE);
mCornerPaint.setStrokeWidth(UIUtil.dip2px(context, 5));
mCornerPaint.setColor(Color.WHITE);
mScaleRadius = UIUtil.dip2px(context, 24);
mBorderThickness = UIUtil.dip2px(context, 3);
mCornerThickness = UIUtil.dip2px(context, 5);
mCornerLength = UIUtil.dip2px(context, 20);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mBitmapRect = getBitmapRect();
initCropWindow(mBitmapRect);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制九宫格引导线
// drawGuidelines(canvas);
//绘制裁剪边框
drawBorder(canvas);
//绘制裁剪边框的四个角
drawCorners(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onActionDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
onActionUp();
return true;
case MotionEvent.ACTION_MOVE:
onActionMove(event.getX(), event.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return true;
default:
return false;
}
}
/**
* 获取裁剪好的BitMap
*/
public BitmapgetCroppedImage() {
final Drawable drawable = getDrawable();
if (!(drawableinstanceof BitmapDrawable)) {
return null;
}
final Bitmap originalBitmap = ((BitmapDrawable) drawable).getBitmap();
double aY = originalBitmap.getHeight() / (float) (mBitmapRect.bottom -mBitmapRect.top);
double aX = originalBitmap.getWidth() / (float) (mBitmapRect.right -mBitmapRect.left);
float cropX = (float) (aX * (Edge.LEFT.getCoordinate() -mBitmapRect.left));
float cropY = (float) (aY * (Edge.TOP.getCoordinate() -mBitmapRect.top));
final float cropWidth = (float) (aX * Edge.getWidth());
final float cropHeight = (float) (aY * Edge.getHeight());
return Bitmap.createBitmap(originalBitmap,
(int) cropX,
(int) cropY,
(int) cropWidth,
(int) cropHeight
);
}
/**
* 获取图片ImageView周围的边界组成的RectF对象
*/
private RectFgetBitmapRect() {
final Drawable drawable = getDrawable();
if (drawable ==null) {
return new RectF();
}
final float[] matrixValues =new float[9];
getImageMatrix().getValues(matrixValues);
final float scaleX = matrixValues[Matrix.MSCALE_X];
final float scaleY = matrixValues[Matrix.MSCALE_Y];
final float transX = matrixValues[Matrix.MTRANS_X];
final float transY = matrixValues[Matrix.MTRANS_Y];
final int drawableIntrinsicWidth = drawable.getIntrinsicWidth();
final int drawableIntrinsicHeight = drawable.getIntrinsicHeight();
final int drawableDisplayWidth = Math.round(drawableIntrinsicWidth * scaleX);
final int drawableDisplayHeight = Math.round(drawableIntrinsicHeight * scaleY);
final float left = Math.max(transX, 0);
final float top = Math.max(transY, 0);
final float right = Math.min(left + drawableDisplayWidth, getWidth());
final float bottom = Math.min(top + drawableDisplayHeight, getHeight());
return new RectF(left, top, right, bottom);
}
/**
* 初始化裁剪框
*
* @param bitmapRect
*/
private boolean isOne =true;
private void initCropWindow(@NonNull RectF bitmapRect) {
if (isOne) {
odlBitmapRect = bitmapRect;
oldW = bitmapRect.right - bitmapRect.left;
oldH = bitmapRect.bottom - bitmapRect.top;
isOne =false;
//裁剪框距离图片左右的padding值
final float horizontalPadding =0.2f * bitmapRect.width();
final float verticalPadding =0.2f * bitmapRect.height();
// if (getHeight() > getWidth()) {
// Edge.LEFT.initCoordinate(bitmapRect.left - ((bitmapRect.left - bitmapRect.right) / 4f));
// Edge.TOP.initCoordinate(((bitmapRect.top + bitmapRect.bottom) / 2f) - ((bitmapRect.left - bitmapRect.right) / 8f));
// Edge.RIGHT.initCoordinate(bitmapRect.right + ((bitmapRect.left - bitmapRect.right) / 4f));
// Edge.BOTTOM.initCoordinate(((bitmapRect.top + bitmapRect.bottom) / 2f )+ ((bitmapRect.left - bitmapRect.right) / 8f));
// }else {
//
// }
//初始化裁剪框上下左右四条边
Edge.LEFT.initCoordinate(bitmapRect.left + horizontalPadding);
Edge.TOP.initCoordinate(bitmapRect.top + verticalPadding);
Edge.RIGHT.initCoordinate(bitmapRect.right - horizontalPadding);
Edge.BOTTOM.initCoordinate(bitmapRect.bottom - verticalPadding);
}else {
//变形后的宽高0>90 90->180 180->270 270->360
float newW = bitmapRect.right - bitmapRect.left;
float newH = bitmapRect.bottom - bitmapRect.top;
float newWB = newH /oldW;
float newWH = newW /oldH;
float newT = bitmapRect.top + (newWB * (Edge.LEFT.getCoordinate() -odlBitmapRect.left));
float newR = bitmapRect.right - (newWH * (Edge.TOP.getCoordinate() -odlBitmapRect.top));
float newB = bitmapRect.bottom - (newWB * (odlBitmapRect.right - Edge.RIGHT.getCoordinate()));
float newL = bitmapRect.left + (newWH * (odlBitmapRect.bottom - Edge.BOTTOM.getCoordinate()));
Edge.TOP.initCoordinate(newT);
Edge.RIGHT.initCoordinate(newR);
Edge.BOTTOM.initCoordinate(newB);
Edge.LEFT.initCoordinate(newL);
odlBitmapRect = bitmapRect;
oldW = bitmapRect.right - bitmapRect.left;
oldH = bitmapRect.bottom - bitmapRect.top;
}
}
private void drawGuidelines(@NonNull Canvas canvas) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
final float oneThirdCropWidth = Edge.getWidth() /3;
final float x1 = left + oneThirdCropWidth;
//引导线竖直方向第一条线
canvas.drawLine(x1, top, x1, bottom, mGuidelinePaint);
final float x2 = right - oneThirdCropWidth;
//引导线竖直方向第二条线
canvas.drawLine(x2, top, x2, bottom, mGuidelinePaint);
final float oneThirdCropHeight = Edge.getHeight() /3;
final float y1 = top + oneThirdCropHeight;
//引导线水平方向第一条线
canvas.drawLine(left, y1, right, y1, mGuidelinePaint);
final float y2 = bottom - oneThirdCropHeight;
//引导线水平方向第二条线
canvas.drawLine(left, y2, right, y2, mGuidelinePaint);
}
//画4边蒙版
private void drawBorder(@NonNull Canvas canvas) {
//上蒙版
canvas.drawRect(0f, 0f, getWidth(), Edge.TOP.getCoordinate(), mBorderPaint);
//左蒙版
canvas.drawRect(0f, Edge.TOP.getCoordinate() -0.22f, Edge.LEFT.getCoordinate(), Edge.BOTTOM.getCoordinate() +0.22f, mBorderPaint);
//下蒙版
canvas.drawRect(0f, Edge.BOTTOM.getCoordinate(), getWidth(), getHeight(), mBorderPaint);
//右蒙版
canvas.drawRect(Edge.RIGHT.getCoordinate(), Edge.TOP.getCoordinate() -0.22f, getWidth(), Edge.BOTTOM.getCoordinate() +0.22f, mBorderPaint);
}
private void drawCorners(@NonNull Canvas canvas) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
//简单的数学计算
final float lateralOffset = (mCornerThickness -mBorderThickness) /2f;
final float startOffset =mCornerThickness - (mBorderThickness /2f);
mCornerPaint.setStrokeCap(Paint.Cap.ROUND);
mCornerPaint.setStrokeJoin(Paint.Join.ROUND);
//左上角
Path path1 =new Path();
path1.moveTo(left - lateralOffset, top +mCornerLength);
path1.lineTo(left - lateralOffset, top - lateralOffset);
path1.lineTo(left +mCornerLength, top - lateralOffset);
canvas.drawPath(path1, mCornerPaint);
//右上角
Path path2 =new Path();
path2.moveTo(right -mCornerLength, top - lateralOffset);
path2.lineTo(right + lateralOffset, top - lateralOffset);
path2.lineTo(right + lateralOffset, top +mCornerLength);
canvas.drawPath(path2, mCornerPaint);
// //右上角右面的短线
// canvas.drawLine(right + lateralOffset, top - startOffset, right + lateralOffset, top + mCornerLength, mCornerPaint);
// //右上角上面的短线
// canvas.drawLine(right + startOffset, top - lateralOffset, right - mCornerLength, top - lateralOffset, mCornerPaint);
//左下角
Path path3 =new Path();
path3.moveTo(left - lateralOffset, bottom -mCornerLength);
path3.lineTo(left - lateralOffset, bottom + lateralOffset);
path3.lineTo(left +mCornerLength, bottom + lateralOffset);
canvas.drawPath(path3, mCornerPaint);
// canvas.drawLine(left - lateralOffset, bottom + startOffset, left - lateralOffset, bottom - mCornerLength, mCornerPaint);
// //左下角底部的短线
// canvas.drawLine(left - startOffset, bottom + lateralOffset, left + mCornerLength, bottom + lateralOffset, mCornerPaint);
//右下角左面的短线
Path path4 =new Path();
path4.moveTo(right + lateralOffset, bottom -mCornerLength);
path4.lineTo(right + lateralOffset, bottom + lateralOffset);
path4.lineTo(right -mCornerLength, bottom + lateralOffset);
canvas.drawPath(path4, mCornerPaint);
// canvas.drawLine(right + lateralOffset, bottom + startOffset, right + lateralOffset, bottom - mCornerLength, mCornerPaint);
// //右下角底部的短线
// canvas.drawLine(right + startOffset, bottom + lateralOffset, right - mCornerLength, bottom + lateralOffset, mCornerPaint);
}
/**
* 处理手指按下事件
*
* @param x 手指按下时水平方向的坐标
* @param y 手指按下时竖直方向的坐标
*/
private void onActionDown(float x, float y) {
//获取边框的上下左右四个坐标点的坐标
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
//获取手指所在位置位于图二种的A,B,C,D位置种哪一种
mPressedCropWindowEdgeSelector = CatchEdgeUtil.getPressedHandle(x, y, left, top, right, bottom, mScaleRadius);
if (mPressedCropWindowEdgeSelector !=null) {
//计算手指按下的位置与裁剪框的偏移量
CatchEdgeUtil.getOffset(mPressedCropWindowEdgeSelector, x, y, left, top, right, bottom, mTouchOffset);
invalidate();
}
}
private void onActionUp() {
if (mPressedCropWindowEdgeSelector !=null) {
mPressedCropWindowEdgeSelector =null;
invalidate();
}
}
private void onActionMove(float x, float y) {
if (mPressedCropWindowEdgeSelector ==null) {
return;
}
x +=mTouchOffset.x;
y +=mTouchOffset.y;
mPressedCropWindowEdgeSelector.updateCropWindow(x, y, mBitmapRect);
invalidate();
}
}
```