美文网首页
仿作业帮图片裁剪-自定义View

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

作者: Android小豆渣 | 来源:发表于2022-02-21 09:14 被阅读0次

    效果图:

    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;
        }
    }
    

    相关文章

      网友评论

          本文标题:仿作业帮图片裁剪-自定义View

          本文链接:https://www.haomeiwen.com/subject/bavblrtx.html