Android自定义不规则裁剪

Posted by 卢小胖 on 2021-02-20
Estimated Reading Time 18 Minutes
Words 3.2k In Total
Viewed Times

需求:实现一个不规则裁剪View,以前的实现方式不太优雅,和布局嵌套在一起耦合性太高,所以想将裁剪框和图片封装在一个View中。

实现效果如下

demo

直接将代码,不多BB

具体请看Git demo,欢迎Star,fork
package com.xlu.cropimage;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;



public class CropImageView extends androidx.appcompat.widget.AppCompatImageView {

private static final String TAG = "CropImageView";

private static final float TOUCH_POINT_CATCH_DISTANCE = 15; //dp,触摸点捕捉到锚点的最小距离
private static final float POINT_RADIUS = 10; // dp,锚点绘制半径
private static final float MAGNIFIER_CROSS_LINE_WIDTH = 1.0f; //dp,放大镜十字宽度
private static final float MAGNIFIER_CROSS_LINE_LENGTH = 5; //dp, 放大镜十字长度
private static final float MAGNIFIER_BORDER_WIDTH = 1; //dp,放大镜边框宽度

private static final int DEFAULT_LINE_COLOR = 0xFF43CAD5;//线条颜色
private static final float DEFAULT_LINE_WIDTH = 1; //dp
private static final int DEFAULT_MASK_ALPHA = 80; // 0 - 255
private static final int DEFAULT_MAGNIFIER_CROSS_COLOR = 0x51FF2D55;
private static final float DEFAULT_GUIDE_LINE_WIDTH = 0.3f;//dp
private static final int DEFAULT_GUIDE_LINE_COLOR = Color.WHITE;
private static final int DEFAULT_POINT_FILL_COLOR = Color.WHITE;
private static final int DEFAULT_POINT_FILL_ALPHA = 180;

private Paint mPointPaint;
private Paint mPointFillPaint;
private Paint mLinePaint;
private Paint mMaskPaint;
private Paint mGuideLinePaint;
private Paint mMagnifierPaint;
private Paint mMagnifierCrossPaint;
private float mScaleX, mScaleY; // 显示的图片与实际图片缩放比
private int mActWidth, mActHeight, mActLeft, mActTop; //实际显示图片的位置
private Point mDraggingPoint = null;
private float mDensity;
private ShapeDrawable mMagnifierDrawable;

private float[] mMatrixValue = new float[9];
private Xfermode mMaskXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
private Path mPointLinePath = new Path();
private Matrix mMagnifierMatrix = new Matrix();

Point[] mCropPoints; // 裁剪区域, 0->LeftTop, 1->RightTop, 2->RightBottom, 3->LeftBottom
Point[] mEdgeMidPoints; //边中点
float mLineWidth; // 选区线的宽度
int mPointColor; //锚点颜色
float mPointWidth; //锚点宽度
float mGuideLineWidth; // 辅助线宽度
int mPointFillColor = DEFAULT_POINT_FILL_COLOR; // 锚点内部填充颜色
int mPointFillAlpha = DEFAULT_POINT_FILL_ALPHA; // 锚点填充颜色透明度
int mLineColor = DEFAULT_LINE_COLOR; // 选区线的颜色
int mMagnifierCrossColor = DEFAULT_MAGNIFIER_CROSS_COLOR; // 放大镜十字颜色
int mGuideLineColor = DEFAULT_GUIDE_LINE_COLOR; // 辅助线颜色
int mMaskAlpha = DEFAULT_MASK_ALPHA; //0 - 255, 蒙版透明度
boolean mShowGuideLine = true; // 是否显示辅助线
boolean mShowMagnifier = true;// 是否显示放大镜
boolean mShowEdgeMidPoint = true;//是否显示边中点

boolean mDragLimit = true;// 是否限制锚点拖动范围为凸四边形

enum DragPointType{
LEFT_TOP,
RIGHT_TOP,
RIGHT_BOTTOM,
LEFT_BOTTOM,
TOP,
RIGHT,
BOTTOM,
LEFT;

public static boolean isEdgePoint(DragPointType type){
return type == TOP || type == RIGHT || type == BOTTOM || type == LEFT;
}
}

private final static int P_LT = 0, P_RT = 1, P_RB = 2, P_LB = 3;

public CropImageView(Context context) {
this(context, null);
}

public CropImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public CropImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ScaleType scaleType = getScaleType();
if (scaleType == ScaleType.FIT_END || scaleType == ScaleType.FIT_START || scaleType == ScaleType.MATRIX) {
throw new RuntimeException("Image in CropImageView must be in center");
}
mDensity = getResources().getDisplayMetrics().density;
initAttrs(context, attrs);
initPaints();
}

private void initAttrs(Context context, AttributeSet attrs) {
mMaskAlpha = Math.min(Math.max(0, DEFAULT_MASK_ALPHA), 255);
mLineColor = DEFAULT_LINE_COLOR;
mLineWidth = dp2px(DEFAULT_LINE_WIDTH);
mPointColor = DEFAULT_LINE_COLOR;
mPointWidth = dp2px(DEFAULT_LINE_WIDTH);
mMagnifierCrossColor = DEFAULT_MAGNIFIER_CROSS_COLOR;
mGuideLineWidth = dp2px(DEFAULT_GUIDE_LINE_WIDTH);
mGuideLineColor = DEFAULT_GUIDE_LINE_COLOR;
mPointFillColor = DEFAULT_POINT_FILL_COLOR;
mPointFillAlpha = Math.min(Math.max(0, DEFAULT_POINT_FILL_ALPHA), 255);
}


/**
* 设置选区
* @param cropPoints 选区顶点
*/
public void setCropPoints(Point[] cropPoints) {
if (getDrawable() == null) {
Log.w(TAG, "should call after set drawable");
return;
}
if (!checkPoints(cropPoints)) {
setFullImgCrop();
} else {
this.mCropPoints = cropPoints;
invalidate();
}
}

public void setEdgeMidPoints(){
if (mEdgeMidPoints == null){
mEdgeMidPoints = new Point[4];
for (int i = 0; i < mEdgeMidPoints.length; i++){
mEdgeMidPoints[i] = new Point();
}
}
int len = mCropPoints.length;
for (int i = 0; i < len; i++){
mEdgeMidPoints[i].set(mCropPoints[i].x + (mCropPoints[(i+1)%len].x - mCropPoints[i].x)/2,
mCropPoints[i].y + (mCropPoints[(i+1)%len].y - mCropPoints[i].y)/2);
}
}

/**
* 设置选区为包裹全图
*/
public void setFullImgCrop() {
if (getDrawable() == null) {
Log.w(TAG, "should call after set drawable");
return;
}
this.mCropPoints = getFullImgCropPoints();
invalidate();
}

@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mMagnifierDrawable = null;
}

/**
* 设置待裁剪图片并显示
* @param bmp 待裁剪图片
*/
public void setImageToCrop(Bitmap bmp) {
setImageBitmap(bmp);
setCropPoints(null);
}

/**
* 获取选区
* @return 选区顶点
*/
public Point[] getCropPoints() {
return mCropPoints;
}

/**
* 设置锚点填充颜色
* @param pointFillColor 颜色
*/
public void setPointFillColor(int pointFillColor) {
this.mPointFillColor = pointFillColor;
}

/**
* 设置锚点填充颜色透明度
* @param pointFillAlpha 透明度
*/
public void setPointFillAlpha(int pointFillAlpha) {
this.mPointFillAlpha = pointFillAlpha;
}

/**
* 蒙版透明度
* @param maskAlpha 透明度
*/
public void setMaskAlpha(int maskAlpha) {
maskAlpha = Math.min(Math.max(0, maskAlpha), 255);
this.mMaskAlpha = maskAlpha;
invalidate();
}

/**
* 是否显示辅助线
* @param showGuideLine 是否
*/
public void setShowGuideLine(boolean showGuideLine) {
this.mShowGuideLine = showGuideLine;
invalidate();
}

/**
* 设置辅助线颜色
* @param guideLineColor 颜色
*/
public void setGuideLineColor(int guideLineColor) {
this.mGuideLineColor = guideLineColor;
}

/**
* 设置辅助线宽度
* @param guideLineWidth 宽度 px
*/
public void setGuideLineWidth(float guideLineWidth) {
this.mGuideLineWidth = guideLineWidth;
}

/**
* 设置选区线的颜色
* @param lineColor 颜色
*/
public void setLineColor(int lineColor) {
this.mLineColor = lineColor;
invalidate();
}

/**
* 设置放大镜准心颜色
* @param magnifierCrossColor 准心颜色
*/
public void setMagnifierCrossColor(int magnifierCrossColor) {
this.mMagnifierCrossColor = magnifierCrossColor;
}

/**
* 设置选区线宽度
* @param lineWidth 线宽度,px
*/
public void setLineWidth(int lineWidth) {
this.mLineWidth = lineWidth;
invalidate();
}

public void setPointColor(int pointColor) {
this.mPointColor = pointColor;
invalidate();
}

public void setPointWidth(float pointWidth) {
this.mPointWidth = pointWidth;
invalidate();
}

/**
* 设置是否显示放大镜
* @param showMagnifier 是否
*/
public void setShowMagnifier(boolean showMagnifier) {
this.mShowMagnifier = showMagnifier;
}


/**
* 设置是否限制拖动为凸四边形
* @param dragLimit 是否
*/
public void setDragLimit(boolean dragLimit) {
this.mDragLimit = dragLimit;
}


/**
* 选区是否为凸四边形
* @return true:凸四边形
*/
public boolean canRightCrop() {
if (!checkPoints(mCropPoints)) {
return false;
}
Point lt = mCropPoints[0];
Point rt = mCropPoints[1];
Point rb = mCropPoints[2];
Point lb = mCropPoints[3];
return (pointSideLine(lt, rb, lb) * pointSideLine(lt, rb, rt) < 0) && (pointSideLine(lb, rt, lt) * pointSideLine(lb, rt, rb) < 0);
}

public boolean checkPoints(Point[] points) {
return points != null && points.length == 4
&& points[0] != null && points[1] != null && points[2] != null && points[3] != null;
}

private long pointSideLine(Point lineP1, Point lineP2, Point point) {
return pointSideLine(lineP1, lineP2, point.x, point.y);
}

private long pointSideLine(Point lineP1, Point lineP2, int x, int y) {
long x1 = lineP1.x;
long y1 = lineP1.y;
long x2 = lineP2.x;
long y2 = lineP2.y;
return (x - x1)*(y2 - y1) - (y - y1)*(x2 - x1);
}

public Bitmap getBitmap() {
Bitmap bmp = null;
Drawable drawable = getDrawable();
if (drawable instanceof BitmapDrawable) {
bmp = ((BitmapDrawable)drawable).getBitmap();
}
return bmp;
}

private void initPaints() {
mPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPointPaint.setColor(mPointColor);
mPointPaint.setStrokeWidth(mPointWidth);
mPointPaint.setStyle(Paint.Style.STROKE);

mPointFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPointFillPaint.setColor(mPointFillColor);
mPointFillPaint.setStyle(Paint.Style.FILL);
mPointFillPaint.setAlpha(mPointFillAlpha);

mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//mLinePaint.setColor(mLineColor);
mLinePaint.setColor(mPointColor);
mLinePaint.setStrokeWidth(mLineWidth);
mLinePaint.setStyle(Paint.Style.STROKE);

mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mMaskPaint.setColor(Color.BLACK);
mMaskPaint.setStyle(Paint.Style.FILL);

mGuideLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mGuideLinePaint.setColor(mGuideLineColor);
mGuideLinePaint.setStyle(Paint.Style.FILL);
mGuideLinePaint.setStrokeWidth(mGuideLineWidth);

mMagnifierPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mMagnifierPaint.setColor(Color.WHITE);
mMagnifierPaint.setStyle(Paint.Style.FILL);

mMagnifierCrossPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mMagnifierCrossPaint.setColor(mMagnifierCrossColor);
mMagnifierCrossPaint.setStyle(Paint.Style.FILL);
mMagnifierCrossPaint.setStrokeWidth(dp2px(MAGNIFIER_CROSS_LINE_WIDTH));
}

private void initMagnifier() {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(getBitmap(), null, new Rect(mActLeft, mActTop, mActWidth + mActLeft, mActHeight + mActTop), null);
canvas.save();
BitmapShader magnifierShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mMagnifierDrawable = new ShapeDrawable(new OvalShape());
mMagnifierDrawable.getPaint().setShader(magnifierShader);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//初始化图片位置信息
getDrawablePosition();
//开始绘制选区
onDrawCropPoint(canvas);
}

protected void onDrawCropPoint(Canvas canvas) {
//绘制蒙版
onDrawMask(canvas);
//绘制辅助线
onDrawGuideLine(canvas);
//绘制选区线
onDrawLines(canvas);
//绘制锚点
onDrawPoints(canvas);
//绘制放大镜
onDrawMagnifier(canvas);
// onDrawCusMagnifier(canvas);
}

protected void onDrawCusMagnifier(Canvas canvas){
DragPointType pointType = getPointType(mDraggingPoint);
if (pointType == null || DragPointType.isEdgePoint(pointType)){
return;
}

if (mShowMagnifier && mDraggingPoint != null) {
if (mMagnifierDrawable == null) {
initMagnifier();
}
float draggingX = getViewPointX(mDraggingPoint);
float draggingY = getViewPointY(mDraggingPoint);
float radius = getWidth() / 8;
float cx = radius; //圆心x坐标
int lineOffset = (int) dp2px(MAGNIFIER_BORDER_WIDTH);
if (0 <= mDraggingPoint.x && mDraggingPoint.x < getDrawable().getIntrinsicWidth() / 2){ //拉伸点在左侧时,放大镜显示在右侧
mMagnifierDrawable.setBounds(getWidth() - (int)radius * 2 + lineOffset, lineOffset, getWidth() - lineOffset, (int)radius * 2 - lineOffset);
cx = getWidth() - radius;
} else {
mMagnifierDrawable.setBounds(lineOffset, lineOffset, (int)radius * 2 - lineOffset, (int)radius * 2 - lineOffset);
}
canvas.drawCircle(cx, radius, radius, mMagnifierPaint);
mMagnifierMatrix.setTranslate(radius - draggingX, radius - draggingY);
mMagnifierDrawable.getPaint().getShader().setLocalMatrix(mMagnifierMatrix);
mMagnifierDrawable.draw(canvas);
//放大镜锚点
canvas.drawCircle(cx, radius, dp2px(POINT_RADIUS), mPointFillPaint);
canvas.drawCircle(cx, radius, dp2px(POINT_RADIUS), mPointPaint);
}
}

protected void onDrawMagnifier(Canvas canvas) {
if (mShowMagnifier && mDraggingPoint != null) {
if (mMagnifierDrawable == null) {
initMagnifier();
}
float draggingX = getViewPointX(mDraggingPoint);
float draggingY = getViewPointY(mDraggingPoint);

float radius = getWidth() / 8;
float cx = radius;
int lineOffset = (int) dp2px(MAGNIFIER_BORDER_WIDTH);
mMagnifierDrawable.setBounds(lineOffset, lineOffset, (int)radius * 2 - lineOffset, (int)radius * 2 - lineOffset);
double pointsDistance = getPointsDistance(draggingX, draggingY, 0, 0);
if (pointsDistance < (radius * 2.5)) {
mMagnifierDrawable.setBounds(getWidth() - (int)radius * 2 + lineOffset, lineOffset, getWidth() - lineOffset, (int)radius * 2 - lineOffset);
cx = getWidth() - radius;
}
canvas.drawCircle(cx, radius, radius, mMagnifierPaint);
mMagnifierMatrix.setTranslate(radius - draggingX, radius - draggingY);
mMagnifierDrawable.getPaint().getShader().setLocalMatrix(mMagnifierMatrix);
mMagnifierDrawable.draw(canvas);
float crossLength = dp2px(MAGNIFIER_CROSS_LINE_LENGTH);
canvas.drawLine(cx, radius - crossLength, cx, radius + crossLength, mMagnifierCrossPaint);
canvas.drawLine(cx - crossLength, radius, cx + crossLength, radius, mMagnifierCrossPaint);
}
}

protected void onDrawGuideLine(Canvas canvas) {
if (!mShowGuideLine) {
return;
}
int widthStep = mActWidth / 3;
int heightStep = mActHeight / 3;
canvas.drawLine(mActLeft + widthStep, mActTop, mActLeft + widthStep, mActTop + mActHeight, mGuideLinePaint);
canvas.drawLine(mActLeft + widthStep * 2, mActTop, mActLeft + widthStep * 2, mActTop + mActHeight, mGuideLinePaint);
canvas.drawLine(mActLeft, mActTop + heightStep, mActLeft + mActWidth, mActTop + heightStep, mGuideLinePaint);
canvas.drawLine(mActLeft, mActTop + heightStep * 2, mActLeft + mActWidth, mActTop + heightStep * 2, mGuideLinePaint);
}

protected void onDrawMask(Canvas canvas) {
if (mMaskAlpha <= 0) {
return;
}
Path path = resetPointPath();
if (path != null) {
int sc = canvas.saveLayer(mActLeft, mActTop, mActLeft + mActWidth, mActTop + mActHeight, mMaskPaint, Canvas.ALL_SAVE_FLAG);
mMaskPaint.setAlpha(mMaskAlpha);
canvas.drawRect(mActLeft, mActTop, mActLeft + mActWidth, mActTop + mActHeight, mMaskPaint);
mMaskPaint.setXfermode(mMaskXfermode);
mMaskPaint.setAlpha(255);
canvas.drawPath(path, mMaskPaint);
mMaskPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
}

private Path resetPointPath() {
if (!checkPoints(mCropPoints)) {
return null;
}
mPointLinePath.reset();
Point lt = mCropPoints[0];
Point rt = mCropPoints[1];
Point rb = mCropPoints[2];
Point lb = mCropPoints[3];
mPointLinePath.moveTo(getViewPointX(lt),getViewPointY(lt));
mPointLinePath.lineTo(getViewPointX(rt),getViewPointY(rt));
mPointLinePath.lineTo(getViewPointX(rb),getViewPointY(rb));
mPointLinePath.lineTo(getViewPointX(lb),getViewPointY(lb));
mPointLinePath.close();
return mPointLinePath;
}

private void getDrawablePosition() {
Drawable drawable = getDrawable();
if (drawable != null) {
getImageMatrix().getValues(mMatrixValue);
mScaleX = mMatrixValue[Matrix.MSCALE_X];
mScaleY = mMatrixValue[Matrix.MSCALE_Y];
int origW = drawable.getIntrinsicWidth();
int origH = drawable.getIntrinsicHeight();
mActWidth = Math.round(origW * mScaleX);
mActHeight = Math.round(origH * mScaleY);
mActLeft = (getWidth() - mActWidth) / 2;
mActTop = (getHeight() - mActHeight) / 2;
}
}

protected void onDrawLines(Canvas canvas) {
Path path = resetPointPath();
if (path != null) {
canvas.drawPath(path, mLinePaint);
}
}

protected void onDrawPoints(Canvas canvas) {
if (!checkPoints(mCropPoints)) {
return;
}
for (Point point : mCropPoints) {
canvas.drawCircle(getViewPointX(point), getViewPointY(point), dp2px(POINT_RADIUS), mPointFillPaint);
canvas.drawCircle(getViewPointX(point), getViewPointY(point), dp2px(POINT_RADIUS), mPointPaint);
}
if (mShowEdgeMidPoint) {
setEdgeMidPoints();
//中间锚点
for (Point point : mEdgeMidPoints){
canvas.drawCircle(getViewPointX(point), getViewPointY(point), dp2px(POINT_RADIUS), mPointFillPaint);
canvas.drawCircle(getViewPointX(point), getViewPointY(point), dp2px(POINT_RADIUS), mPointPaint);
}
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
boolean handle = true;
switch (action) {
case MotionEvent.ACTION_DOWN:
mDraggingPoint = getNearbyPoint(event);
if (mDraggingPoint == null) {
handle = false;
}
break;
case MotionEvent.ACTION_MOVE:
toImagePointSize(mDraggingPoint, event);
break;
case MotionEvent.ACTION_UP:
mDraggingPoint = null;
break;
}
invalidate();
return handle || super.onTouchEvent(event);
}

private Point getNearbyPoint(MotionEvent event) {
if (checkPoints(mCropPoints)) {
for (Point p : mCropPoints) {
if (isTouchPoint(p, event)) return p;
}
}
if (checkPoints(mEdgeMidPoints)) {
for (Point p : mEdgeMidPoints){
if (isTouchPoint(p, event)) return p;
}
}
return null;
}

private boolean isTouchPoint(Point p, MotionEvent event){
float x = event.getX();
float y = event.getY();
float px = getViewPointX(p);
float py = getViewPointY(p);
double distance = Math.sqrt(Math.pow(x - px, 2) + Math.pow(y - py, 2));
if (distance < dp2px(TOUCH_POINT_CATCH_DISTANCE)) {
return true;
}
return false;
}

private void toImagePointSize(Point dragPoint, MotionEvent event) {
if (dragPoint == null) {
return;
}

DragPointType pointType = getPointType(dragPoint);

int x = (int) ((Math.min(Math.max(event.getX(), mActLeft), mActLeft + mActWidth) - mActLeft) / mScaleX);
int y = (int) ((Math.min(Math.max(event.getY(), mActTop), mActTop + mActHeight) - mActTop) / mScaleY);

if (mDragLimit && pointType != null) {
switch (pointType) {
case LEFT_TOP:
if (!canMoveLeftTop(x, y)) return;
break;
case RIGHT_TOP:
if (!canMoveRightTop(x, y)) return;
break;
case RIGHT_BOTTOM:
if (!canMoveRightBottom(x, y)) return;
break;
case LEFT_BOTTOM:
if (!canMoveLeftBottom(x, y)) return;
break;
case TOP:
if (!canMoveLeftTop(x, y) || !canMoveRightTop(x, y)) return;
break;
case RIGHT:
if (!canMoveRightTop(x, y) || !canMoveRightBottom(x, y)) return;
break;
case BOTTOM:
if (!canMoveLeftBottom(x, y) || !canMoveRightBottom(x, y)) return;
break;
case LEFT:
if (!canMoveLeftBottom(x, y) || !canMoveLeftTop(x, y)) return;
break;
default:
break;
}
}

if (DragPointType.isEdgePoint(pointType)){
int xoff = x - dragPoint.x;
int yoff = y - dragPoint.y;
moveEdge(pointType, xoff, yoff);
} else {
dragPoint.y = y;
dragPoint.x = x;
}
}

private void moveEdge(DragPointType type, int xoff, int yoff){
switch (type){
case TOP:
movePoint(mCropPoints[P_LT], 0, yoff);
movePoint(mCropPoints[P_RT], 0, yoff);
break;
case RIGHT:
movePoint(mCropPoints[P_RT], xoff, 0);
movePoint(mCropPoints[P_RB], xoff, 0);
break;
case BOTTOM:
movePoint(mCropPoints[P_LB], 0, yoff);
movePoint(mCropPoints[P_RB], 0, yoff);
break;
case LEFT:
movePoint(mCropPoints[P_LT], xoff, 0);
movePoint(mCropPoints[P_LB], xoff, 0);
break;
default: break;
}
}

private void movePoint(Point point, int xoff, int yoff){
if (point == null) return;
int x = point.x + xoff;
int y = point.y + yoff;
if (x < 0 || x > getDrawable().getIntrinsicWidth()) return;
if (y < 0 || y > getDrawable().getIntrinsicHeight()) return;
point.x = x;
point.y = y;
}

private boolean canMoveLeftTop(int x, int y){
if (pointSideLine(mCropPoints[P_RT], mCropPoints[P_LB], x, y)
* pointSideLine(mCropPoints[P_RT], mCropPoints[P_LB], mCropPoints[P_RB]) > 0) {
return false;
}
if (pointSideLine(mCropPoints[P_RT], mCropPoints[P_RB], x, y)
* pointSideLine(mCropPoints[P_RT], mCropPoints[P_RB], mCropPoints[P_LB]) < 0) {
return false;
}
if (pointSideLine(mCropPoints[P_LB], mCropPoints[P_RB], x, y)
* pointSideLine(mCropPoints[P_LB], mCropPoints[P_RB], mCropPoints[P_RT]) < 0) {
return false;
}
return true;
}

private boolean canMoveRightTop(int x, int y){
if (pointSideLine(mCropPoints[P_LT], mCropPoints[P_RB], x, y)
* pointSideLine(mCropPoints[P_LT], mCropPoints[P_RB], mCropPoints[P_LB]) > 0) {
return false;
}
if (pointSideLine(mCropPoints[P_LT], mCropPoints[P_LB], x, y)
* pointSideLine(mCropPoints[P_LT], mCropPoints[P_LB], mCropPoints[P_RB]) < 0) {
return false;
}
if (pointSideLine(mCropPoints[P_LB], mCropPoints[P_RB], x, y)
* pointSideLine(mCropPoints[P_LB], mCropPoints[P_RB], mCropPoints[P_LT]) < 0) {
return false;
}
return true;
}

private boolean canMoveRightBottom(int x, int y){
if (pointSideLine(mCropPoints[P_RT], mCropPoints[P_LB], x, y)
* pointSideLine(mCropPoints[P_RT], mCropPoints[P_LB], mCropPoints[P_LT]) > 0) {
return false;
}
if (pointSideLine(mCropPoints[P_LT], mCropPoints[P_RT], x, y)
* pointSideLine(mCropPoints[P_LT], mCropPoints[P_RT], mCropPoints[P_LB]) < 0) {
return false;
}
if (pointSideLine(mCropPoints[P_LT], mCropPoints[P_LB], x, y)
* pointSideLine(mCropPoints[P_LT], mCropPoints[P_LB], mCropPoints[P_RT]) < 0) {
return false;
}
return true;
}

private boolean canMoveLeftBottom(int x, int y){
if (pointSideLine(mCropPoints[P_LT], mCropPoints[P_RB], x, y)
* pointSideLine(mCropPoints[P_LT], mCropPoints[P_RB], mCropPoints[P_RT]) > 0) {
return false;
}
if (pointSideLine(mCropPoints[P_LT], mCropPoints[P_RT], x, y)
* pointSideLine(mCropPoints[P_LT], mCropPoints[P_RT], mCropPoints[P_RB]) < 0) {
return false;
}
if (pointSideLine(mCropPoints[P_RT], mCropPoints[P_RB], x, y)
* pointSideLine(mCropPoints[P_RT], mCropPoints[P_RB], mCropPoints[P_LT]) < 0) {
return false;
}
return true;
}

private DragPointType getPointType(Point dragPoint){
if (dragPoint == null) return null;

DragPointType type;
if (checkPoints(mCropPoints)) {
for (int i = 0; i < mCropPoints.length; i++) {
if (dragPoint == mCropPoints[i]) {
type = DragPointType.values()[i];
return type;
}
}
}
if (checkPoints(mEdgeMidPoints)) {
for (int i = 0; i < mEdgeMidPoints.length; i++){
if (dragPoint == mEdgeMidPoints[i]){
type = DragPointType.values()[4+i];
return type;
}
}
}
return null;
}

private float getViewPointX(Point point){
return getViewPointX(point.x);
}

private float getViewPointX(float x) {
return x * mScaleX + mActLeft;
}

private float getViewPointY(Point point){
return getViewPointY(point.y);
}

private float getViewPointY(float y) {
return y * mScaleY + mActTop;
}

private float dp2px(float dp) {
return dp * mDensity;
}

private Point[] getFullImgCropPoints() {
Point[] points = new Point[4];
Drawable drawable = getDrawable();
if (drawable != null) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
points[0] = new Point(0, 0);
points[1] = new Point(width, 0);
points[2] = new Point(width, height);
points[3] = new Point(0, height);
}
return points;
}

private double getPointsDistance(Point p1, Point p2) {
return getPointsDistance(p1.x, p1.y, p2.x, p2.y);
}

private double getPointsDistance(float x1, float y1, float x2, float y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}

}