仿作业帮图片裁剪-自定义View

效果图:

image
public class CropImageView extends 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();

    }

}

辅助工具类:

/***
 * 捕获手指在裁剪框的哪一条边
 */
public class CatchEdgeUtil {


    /**
     * 判断手指是否的位置是否在有效的缩放区域:缩放区域的半径为targetRadius
     * 缩放区域使指:裁剪框的四个角度或者四条边,当手指位置处在某个角
     * 或者某条边的时候,则随着手指的移动对裁剪框进行缩放操作。
     * 如果手指位于裁剪框的内部,则裁剪框随着手指的移动而只进行移动操作。
     * 否则可以判定手指距离裁剪框较远而什么都不做
     */
    public static CropWindowEdgeSelector getPressedHandle(float x,
                                                          float y,
                                                          float left,
                                                          float top,
                                                          float right,
                                                          float bottom,
                                                          float targetRadius) {

        CropWindowEdgeSelector nearestCropWindowEdgeSelector = null;

        //判断手指距离裁剪框哪一个角最近

        //最近距离默认正无穷大
        float nearestDistance = Float.POSITIVE_INFINITY;
        //////////判断手指是否在图二种的A位置:四个角之一/////////////////

        //计算手指距离左上角的距离
        final float distanceToTopLeft = calculateDistance(x, y, left, top);
        if (distanceToTopLeft < nearestDistance) {
            nearestDistance = distanceToTopLeft;
            nearestCropWindowEdgeSelector = CropWindowEdgeSelector.TOP_LEFT;
        }


        //计算手指距离右上角的距离
        final float distanceToTopRight = calculateDistance(x, y, right, top);
        if (distanceToTopRight < nearestDistance) {
            nearestDistance = distanceToTopRight;
            nearestCropWindowEdgeSelector = CropWindowEdgeSelector.TOP_RIGHT;
        }

        //计算手指距离左下角的距离
        final float distanceToBottomLeft = calculateDistance(x, y, left, bottom);
        if (distanceToBottomLeft < nearestDistance) {
            nearestDistance = distanceToBottomLeft;
            nearestCropWindowEdgeSelector = CropWindowEdgeSelector.BOTTOM_LEFT;
        }

        //计算手指距离右下角的距离
        final float distanceToBottomRight = calculateDistance(x, y, right, bottom);
        if (distanceToBottomRight < nearestDistance) {
            nearestDistance = distanceToBottomRight;
            nearestCropWindowEdgeSelector = CropWindowEdgeSelector.BOTTOM_RIGHT;
        }

        //如果手指选中了一个最近的角,并且在缩放范围内则返回这个角
        if (nearestDistance <= targetRadius) {
            return nearestCropWindowEdgeSelector;
        }


        //////////判断手指是否在图二种的C位置:四个边的某条边/////////////////
        if (CatchEdgeUtil.isInHorizontalTargetZone(x, y, left, right, top, targetRadius)) {
            return CropWindowEdgeSelector.TOP;//说明手指在裁剪框top区域
        } else if (CatchEdgeUtil.isInHorizontalTargetZone(x, y, left, right, bottom, targetRadius)) {
            return CropWindowEdgeSelector.BOTTOM;//说明手指在裁剪框bottom区域
        } else if (CatchEdgeUtil.isInVerticalTargetZone(x, y, left, top, bottom, targetRadius)) {
            return CropWindowEdgeSelector.LEFT;//说明手指在裁剪框left区域
        } else if (CatchEdgeUtil.isInVerticalTargetZone(x, y, right, top, bottom, targetRadius)) {
            return CropWindowEdgeSelector.RIGHT;//说明手指在裁剪框right区域
        }


        //////////判断手指是否在图二种的B位置:裁剪框的中间/////////////////
        if (isWithinBounds(x, y, left, top, right, bottom)) {
            return CropWindowEdgeSelector.CENTER;
        }

        ////////手指位于裁剪框的D位置,此时移动手指什么都不做/////////////
        return null;
    }

    public static void getOffset(@NonNull CropWindowEdgeSelector cropWindowEdgeSelector,
                                 float x,
                                 float y,
                                 float left,
                                 float top,
                                 float right,
                                 float bottom,
                                 @NonNull PointF touchOffsetOutput) {

        float touchOffsetX = 0;
        float touchOffsetY = 0;

        switch (cropWindowEdgeSelector) {

            case TOP_LEFT:
                touchOffsetX = left - x;
                touchOffsetY = top - y;
                break;
            case TOP_RIGHT:
                touchOffsetX = right - x;
                touchOffsetY = top - y;
                break;
            case BOTTOM_LEFT:
                touchOffsetX = left - x;
                touchOffsetY = bottom - y;
                break;
            case BOTTOM_RIGHT:
                touchOffsetX = right - x;
                touchOffsetY = bottom - y;
                break;
            case LEFT:
                touchOffsetX = left - x;
                touchOffsetY = 0;
                break;
            case TOP:
                touchOffsetX = 0;
                touchOffsetY = top - y;
                break;
            case RIGHT:
                touchOffsetX = right - x;
                touchOffsetY = 0;
                break;
            case BOTTOM:
                touchOffsetX = 0;
                touchOffsetY = bottom - y;
                break;
            case CENTER:
                final float centerX = (right + left) / 2;
                final float centerY = (top + bottom) / 2;
                touchOffsetX = centerX - x;
                touchOffsetY = centerY - y;
                break;
        }

        touchOffsetOutput.x = touchOffsetX;
        touchOffsetOutput.y = touchOffsetY;
    }


    private static boolean isInHorizontalTargetZone(float x,
                                                    float y,
                                                    float handleXStart,
                                                    float handleXEnd,
                                                    float handleY,
                                                    float targetRadius) {

        return (x > handleXStart && x < handleXEnd && Math.abs(y - handleY) <= targetRadius);
    }


    private static boolean isInVerticalTargetZone(float x,
                                                  float y,
                                                  float handleX,
                                                  float handleYStart,
                                                  float handleYEnd,
                                                  float targetRadius) {

        return (Math.abs(x - handleX) <= targetRadius && y > handleYStart && y < handleYEnd);
    }

    private static boolean isWithinBounds(float x, float y, float left, float top, float right, float bottom) {
        return x >= left && x <= right && y >= top && y <= bottom;
    }

    /**
     * 计算 (x1, y1) 和 (x2, y2)两个点的距离
     */
    private static float calculateDistance(float x1, float y1, float x2, float y2) {

        final float side1 = x2 - x1;
        final float side2 = y2 - y1;

        return (float) Math.sqrt(side1 * side1 + side2 * side2);
    }
}
/**
 * 表示手指选中的裁剪框的哪一个边:有如下几种情况:
 * 手指选中一条边的情况:LEFT,TOP,RIGHT,BOTTOM
 * 手指选中两条边的情况:此时手指位于裁剪框的四个角度的某一个:LEFT and TOP, TOP and RIGHT, RIGHT and BOTTOM, BOTTOM and RIGHT
 * 手指在裁剪框的中间区域,此时移动手指进行的是平移操作
 */
public enum CropWindowEdgeSelector {

    /////////////////////图A///////////////////////////

    //左上角:此时是控制裁剪框最上边和最左边的两条边
    TOP_LEFT(new CropWindowScaleHelper(Edge.TOP, Edge.LEFT)),

    //右上角:此时是控制裁剪框最上边和最右边的两条边
    TOP_RIGHT(new CropWindowScaleHelper(Edge.TOP, Edge.RIGHT)),

    //左下角:此时是控制裁剪框最下边和最左边的两条边
    BOTTOM_LEFT(new CropWindowScaleHelper(Edge.BOTTOM, Edge.LEFT)),

    //右下角:此时是控制裁剪框最下边和最右边的两条边
    BOTTOM_RIGHT(new CropWindowScaleHelper(Edge.BOTTOM, Edge.RIGHT)),

     //////////////////图C/////////////

    //仅控制裁剪框左边线
    LEFT(new CropWindowScaleHelper(null, Edge.LEFT)),

    //仅控制裁剪框右边线
    TOP(new CropWindowScaleHelper(Edge.TOP, null)),

    //仅控制裁剪框上边线
    RIGHT(new CropWindowScaleHelper(null, Edge.RIGHT)),

    //仅控制裁剪框下边线
    BOTTOM(new CropWindowScaleHelper(Edge.BOTTOM, null)),

    //////////////图B//////////////

    //中间位置
    CENTER(new CropWindowMoveHelper());


    private CropWindowScaleHelper mHelper;


    CropWindowEdgeSelector(CropWindowScaleHelper helper) {
        mHelper = helper;
    }

    public void updateCropWindow(float x, float y, @NonNull RectF imageRect) {

        mHelper.updateCropWindow(x, y, imageRect);
    }

}
/**
 * 表示手指再裁剪框里面,此时手指移动表明是移动(平移)裁剪框的操作
 */
class CropWindowMoveHelper extends CropWindowScaleHelper {


    CropWindowMoveHelper() {
        super(null, null);
    }


    @Override
    void updateCropWindow(float x,
                          float y,
                          @NonNull RectF imageRect) {

        //获取裁剪框的四个坐标位置
        float left = Edge.LEFT.getCoordinate();
        float top = Edge.TOP.getCoordinate();
        float right = Edge.RIGHT.getCoordinate();
        float bottom = Edge.BOTTOM.getCoordinate();

        //获取裁剪框的中心位置
        final float currentCenterX = (left + right) / 2;
        final float currentCenterY = (top + bottom) / 2;

        //判断手指移动的距离
        final float offsetX = x - currentCenterX;
        final float offsetY = y - currentCenterY;

        //更新裁剪框四条边的坐标
        Edge.LEFT.offset(offsetX);
        Edge.TOP.offset(offsetY);
        Edge.RIGHT.offset(offsetX);
        Edge.BOTTOM.offset(offsetY);

        //////////////裁剪框越界处理/////////////////

        if (Edge.LEFT.isOutsideMargin(imageRect)) {
            //获取此时x越界时的坐标位置
            float currentCoordinate = Edge.LEFT.getCoordinate();

            //重新指定左边的值为初始值
            Edge.LEFT.initCoordinate(imageRect.left);

            //越界的距离
            float offset = Edge.LEFT.getCoordinate() - currentCoordinate;

            //修正最右边的偏移量
            Edge.RIGHT.offset(offset);
        } else if (Edge.RIGHT.isOutsideMargin(imageRect)) {

            float currentCoordinate = Edge.RIGHT.getCoordinate();

            Edge.RIGHT.initCoordinate(imageRect.right);

            float offset = Edge.RIGHT.getCoordinate() - currentCoordinate;

            Edge.LEFT.offset(offset);
        }


        if (Edge.TOP.isOutsideMargin(imageRect)) {

            float currentCoordinate = Edge.TOP.getCoordinate();

            Edge.TOP.initCoordinate(imageRect.top);

            float offset = Edge.TOP.getCoordinate() - currentCoordinate;

            Edge.BOTTOM.offset(offset);

        } else if (Edge.BOTTOM.isOutsideMargin(imageRect)) {

            float currentCoordinate = Edge.BOTTOM.getCoordinate();

            Edge.BOTTOM.initCoordinate(imageRect.bottom);

            float offset = Edge.BOTTOM.getCoordinate() - currentCoordinate;

            Edge.TOP.offset(offset);
        }
    }

}
/**
 * 操控裁剪框的辅助类:操控裁剪框的缩放
 */
class CropWindowScaleHelper {


    private Edge mHorizontalEdge;
    private Edge mVerticalEdge;


    CropWindowScaleHelper(Edge horizontalEdge, Edge verticalEdge) {
        mHorizontalEdge = horizontalEdge;
        mVerticalEdge = verticalEdge;
    }


    /**
     * 随着手指的移动而改变裁剪框的大小
     *
     * @param x         手指x方向的位置
     * @param y         手指y方向的位置
     * @param imageRect 用来表示图片边界的矩形
     */
    void updateCropWindow(float x,
                          float y,
                          @NonNull RectF imageRect) {

        if (mHorizontalEdge != null)
            mHorizontalEdge.updateCoordinate(x, y, imageRect);

        if (mVerticalEdge != null)
            mVerticalEdge.updateCoordinate(x, y, imageRect);
    }


}
/**
 * 裁剪框上下左右的四个坐标位置:(LEFT,TOP),(LEFT,RIGHT),(RIGHT,BOTTOM),(LEFT,BOTTOM)四个坐标点
 * 组成的矩形就是裁剪框
 */
public enum Edge {

    LEFT,
    TOP,
    RIGHT,
    BOTTOM;

    //裁剪框的最小宽度或者高度
    static final int MIN_CROP_LENGTH_PX = 80;

    //上下左右边界的的值
    private float mCoordinate;


    public void initCoordinate(float coordinate) {
        mCoordinate = coordinate;
    }

    /**
     * 随着手指的移动而改变坐标值
     *
     * @param distance
     */
    public void offset(float distance) {
        mCoordinate += distance;
    }


    public float getCoordinate() {
        return mCoordinate;
    }

    /**
     * 更新某条边的坐标位置
     */
    public void updateCoordinate(float x, float y, @NonNull RectF imageRect) {

        switch (this) {
            case LEFT:
                mCoordinate = adjustLeft(x, imageRect);
                break;
            case TOP:
                mCoordinate = adjustTop(y, imageRect);
                break;
            case RIGHT:
                mCoordinate = adjustRight(x, imageRect);
                break;
            case BOTTOM:
                mCoordinate = adjustBottom(y, imageRect);
                break;
        }
    }


    /**
     * 获取剪切框的宽
     */
    public static float getWidth() {
        return Edge.RIGHT.getCoordinate() - Edge.LEFT.getCoordinate();
    }

    /**
     * 获取剪切框的高
     */
    public static float getHeight() {
        return Edge.BOTTOM.getCoordinate() - Edge.TOP.getCoordinate();
    }

    /**
     * 判断裁剪框是否超越图片指定的边界
     */
    public boolean isOutsideMargin(@NonNull RectF rect) {

        final boolean result;

        switch (this) {
            case LEFT:
                result = mCoordinate - rect.left < 0;
                break;
            case TOP:
                result = mCoordinate - rect.top < 0;
                break;
            case RIGHT:
                result = rect.right - mCoordinate < 0;
                break;
            default: // BOTTOM
                result = rect.bottom - mCoordinate < 0;
                break;
        }
        return result;
    }


    private static float adjustLeft(float x, @NonNull RectF imageRect) {

        final float resultX;
        if (x - imageRect.left < 0) {//左边越界

            resultX = imageRect.left;
        } else {

            //防止裁剪框左边超过右边或者最小范围
            if ((x + MIN_CROP_LENGTH_PX) >= Edge.RIGHT.getCoordinate()) {
                x = Edge.RIGHT.getCoordinate() - MIN_CROP_LENGTH_PX;
            }

            resultX = x;
        }
        return resultX;
    }


    private static float adjustRight(float x, @NonNull RectF imageRect) {

        final float resultX;

        if (imageRect.right - x < 0) {

            resultX = imageRect.right;

        } else {

            //防止裁剪框右边超过最小范围
            if ((x - MIN_CROP_LENGTH_PX) <= Edge.LEFT.getCoordinate()) {
                x = Edge.LEFT.getCoordinate() + MIN_CROP_LENGTH_PX;
            }

            resultX = x;
        }
        return resultX;
    }

    private static float adjustTop(float y, @NonNull RectF imageRect) {

        final float resultY;

        if (y - imageRect.top < 0) {
            resultY = imageRect.top;
        } else {
            //防止裁剪框上边超过最小范围或者越过最下边
            if ((y + MIN_CROP_LENGTH_PX) >= Edge.BOTTOM.getCoordinate()) {
                y = Edge.BOTTOM.getCoordinate() - MIN_CROP_LENGTH_PX;

            }

            resultY = y;
        }
        return resultY;
    }


    private static float adjustBottom(float y, @NonNull RectF imageRect) {

        final float resultY;

        if (imageRect.bottom - y < 0) {
            resultY = imageRect.bottom;
        } else {

            if ((y - MIN_CROP_LENGTH_PX) <= Edge.TOP.getCoordinate()) {
                y = Edge.TOP.getCoordinate() + MIN_CROP_LENGTH_PX;
            }

            resultY = y;
        }
        return resultY;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容