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

效果图:


代码:

```

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();

    }

}

```

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

推荐阅读更多精彩内容