android 利用canvas自由裁剪图片小工具

原文在这里哦~http://www.cnblogs.com/rossoneri/p/3988405.html

因为有个安卓项目需要用到图片裁剪的工具,所以上网查找了一下,发现这个博主写的还不错,但是就是博主太懒了233333,没有写注释,所以我就用了他的代码,理解了代码精髓部分,把我所了解到的代码写了注释,希望帮到大家哦~

原博主写的时可以运行的,但是因为它时原图显示的,太大了,所以我将博主写的一些方法给改了一下,将图片压缩到我想要的大小进行展示和裁剪,另外,原博主写的这个小工具中有个小问题,就是不能将裁剪框拉到最大,不然很容易就会报错,我也进行了一番修改啊,啊哈哈哈

来,上代码:

布局

将这段代码放到你想放的位置就可以啦,这个大小并非时图片显示的大小,但是要给足够的大小来显示图片,不然图片就会被“吃掉啦”。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/scrollview"
  >
<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="270dp"
    android:layout_marginLeft="300dp"
    >
    <com.example.suxiaofang.view.MyCropView
        android:id="@+id/myCropView"
        android:layout_width="500dp"
        android:layout_height="200dp"
        />

</FrameLayout>
</ScrollView>

裁剪的代码

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class MyCropView extends View {
    private static final float BMP_LEFT = 0f;
    private static final float BMP_TOP = 20f;

    private static final float DEFAULT_BORDER_RECT_WIDTH = 100f;
    private static final float DEFAULT_BORDER_RECT_HEIGHT = 100f;

    private static final int POS_TOP_LEFT = 0;
    private static final int POS_TOP_RIGHT = 1;
    private static final int POS_BOTTOM_LEFT = 2;
    private static final int POS_BOTTOM_RIGHT = 3;
    private static final int POS_TOP = 4;
    private static final int POS_BOTTOM = 5;
    private static final int POS_LEFT = 6;
    private static final int POS_RIGHT = 7;
    private static final int POS_CENTER = 8;

    private static final float BORDER_LINE_WIDTH = 6f;
    private static final float BORDER_CORNER_LENGTH = 30f;
    private static final float TOUCH_FIELD = 10f;

    //图片路径
    private String mBmpPath;
    //图片(原图大小)
    private Bitmap mBmpToCrop, currentBmp;
    //经过缩小的图片
    private RectF mBmpBound;
    //画笔
    private Paint mBmpPaint;
    // 裁剪区边框(伸缩框)的笔
    private Paint mBorderPaint;
    private Paint mGuidelinePaint;
    private Paint mCornerPaint;
    private Paint mBgPaint;

    private RectF mDefaultBorderBound;
    private RectF mBorderBound;

    private PointF mLastPoint = new PointF();

    private float mBorderWidth;
    private float mBorderHeight;

    private int touchPos;


    public MyCropView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        init(context);
    }

    public MyCropView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        // super.onSizeChanged(w, h, oldw, oldh);
    }

    //Canvas 画布
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        // super.onDraw(canvas);
        if (mBmpPath != null) {
            //
            canvas.drawBitmap(mBmpToCrop, null, mBmpBound, mBmpPaint);
            //将画布变成方形
            canvas.drawRect(mBorderBound.left, mBorderBound.top, mBorderBound.right, mBorderBound.bottom, mBorderPaint);
            drawGuidlines(canvas);
            drawBackground(canvas);
        }
    }

    //识别裁剪框的伸缩位置
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        // super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setLastPosition(event);
                getParent().requestDisallowInterceptTouchEvent(true);
                // onActionDown(event.getX(), event.getY());
                touchPos = detectTouchPosition(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                onActionMove(event.getX(), event.getY());
                setLastPosition(event);
                break;
            case MotionEvent.ACTION_UP:
                break;
        }

        return true;
    }

    public String getBmpPath() {
        return mBmpPath;
    }

    public void setBmpPath(String picPath) {
        this.mBmpPath = picPath;
        setBmp();
    }

    //缩小原图bitmap,将传来的大小不一致的图片缩小成规定大小
    private static Bitmap smallBitmap(Bitmap bitmap) {
        Matrix matrix = new Matrix();
        //将原图片按照一定的大小比例缩小
        matrix.postScale(0.3f, 0.2f);
        Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizeBmp;
    }


    //得到经过裁剪的图片
    public Bitmap getCroppedImage() {
        Bitmap b = MyCropView.smallBitmap(mBmpToCrop);
        return Bitmap.createBitmap(b, (int) mBorderBound.left, (int) mBorderBound.top, (int) mBorderWidth,
                (int) mBorderHeight);
    }


    //初始化,对各类画笔的初始化
    private void init(Context context) {

        mBmpPaint = new Paint();
        // 以下是抗锯齿
        mBmpPaint.setAntiAlias(true);// 防止边缘的锯齿
        mBmpPaint.setFilterBitmap(true);// 对位图进行滤波处理

        mBorderPaint = new Paint();
        mBorderPaint.setStyle(Style.STROKE);
        mBorderPaint.setColor(Color.parseColor("#AAFFFFFF"));
        mBorderPaint.setStrokeWidth(BORDER_LINE_WIDTH);

        mGuidelinePaint = new Paint();
        mGuidelinePaint.setColor(Color.parseColor("#AAFFFFFF"));
        mGuidelinePaint.setStrokeWidth(1f);

        mCornerPaint = new Paint();

        mBgPaint = new Paint();
        mBgPaint.setColor(Color.parseColor("#B0000000"));
        mBgPaint.setAlpha(150);

    }

    //将要裁剪的图片放置到com.example.suxiaofang.view.MyCropView组件的布局里面,这里需要足够的耐心调哦
    private void setBmp() {
       //这个是原图
        mBmpToCrop = BitmapFactory.decodeFile(mBmpPath);
        //这个就是经过缩小的图片
        currentBmp = MyCropView.smallBitmap(mBmpToCrop);

        //接下来都是设置原图显示的大小
        mBmpBound = new RectF();
        //图片左边距离com.example.suxiaofang.view.MyCropView组件左边边的距离
        mBmpBound.left = BMP_LEFT;
        //图片的上边距离com.example.suxiaofang.view.MyCropView组件上边边的距离
        mBmpBound.top = BMP_TOP;
        //图片的右边距离com.example.suxiaofang.view.MyCropView组件左边边的距离
        mBmpBound.right = currentBmp.getWidth();
        //图片的下边距离com.example.suxiaofang.view.MyCropView组件上边边的距离
        mBmpBound.bottom = currentBmp.getHeight();

        // 使裁剪框一开始出现在图片内自设定的位置,如果裁剪框不在被裁剪的图片内,容易报错
        //其实这也是设定裁剪框的大小
        mDefaultBorderBound = new RectF();
        mDefaultBorderBound.left = (mBmpBound.left + mBmpBound.right) / 3;
        mDefaultBorderBound.top = (mBmpBound.top + mBmpBound.bottom) / 4;
        mDefaultBorderBound.right = mDefaultBorderBound.left + DEFAULT_BORDER_RECT_WIDTH;
        mDefaultBorderBound.bottom = mDefaultBorderBound.top + DEFAULT_BORDER_RECT_HEIGHT;

        mBorderBound = new RectF();
        mBorderBound.left = mDefaultBorderBound.left;
        mBorderBound.top = mDefaultBorderBound.top;
        mBorderBound.right = mDefaultBorderBound.right;
        mBorderBound.bottom = mDefaultBorderBound.bottom;

        getBorderEdgeLength();
        invalidate();
    }

    private void drawBackground(Canvas canvas) {

        /*-
          -------------------------------------
          |                top                |
          -------------------------------------
          |      |                    |       |<——————————mBmpBound
          |      |                    |       |
          | left |                    | right |
          |      |                    |       |
          |      |                  <─┼───────┼────mBorderBound
          -------------------------------------
          |              bottom               |
          -------------------------------------
         */

        // Draw "top", "bottom", "left", then "right" quadrants.
        // because the border line width is larger than 1f, in order to draw a complete border rect ,
        // i have to change zhe rect coordinate to draw
        float delta = BORDER_LINE_WIDTH / 2;
        float left = mBorderBound.left - delta;
        float top = mBorderBound.top - delta;
        float right = mBorderBound.right + delta;
        float bottom = mBorderBound.bottom + delta;

        // -------------------------------------------------------------------------------移动到上下两端会多出来阴影
        canvas.drawRect(mBmpBound.left, mBmpBound.top, mBmpBound.right, top, mBgPaint);
        canvas.drawRect(mBmpBound.left, bottom, mBmpBound.right, mBmpBound.bottom, mBgPaint);
        canvas.drawRect(mBmpBound.left, top, left, bottom, mBgPaint);
        canvas.drawRect(right, top, mBmpBound.right, bottom, mBgPaint);
    }

    // 画裁剪区域中间的参考线
    private void drawGuidlines(Canvas canvas) {
        // Draw vertical guidelines.
        final float oneThirdCropWidth = mBorderBound.width() / 3;

        final float x1 = mBorderBound.left + oneThirdCropWidth;
        canvas.drawLine(x1, mBorderBound.top, x1, mBorderBound.bottom, mGuidelinePaint);
        final float x2 = mBorderBound.right - oneThirdCropWidth;
        canvas.drawLine(x2, mBorderBound.top, x2, mBorderBound.bottom, mGuidelinePaint);

        // Draw horizontal guidelines.
        final float oneThirdCropHeight = mBorderBound.height() / 3;

        final float y1 = mBorderBound.top + oneThirdCropHeight;
        canvas.drawLine(mBorderBound.left, y1, mBorderBound.right, y1, mGuidelinePaint);
        final float y2 = mBorderBound.bottom - oneThirdCropHeight;
        canvas.drawLine(mBorderBound.left, y2, mBorderBound.right, y2, mGuidelinePaint);
    }

    private void onActionDown(float x, float y) {

    }

    //移动裁剪框时定位到的位置大小
    private void onActionMove(float x, float y) {
        Log.e("mBmpBound.right~~~~~~~", String.valueOf(mBmpBound.right));
        Log.e("(mBorderBound.right~~~~~~~", String.valueOf(mBorderBound.right));
        float deltaX = x - mLastPoint.x;
        float deltaY = y - mLastPoint.y;
        // 这里先不考虑裁剪框放最大的情况【这个就是原博主没有考虑到将裁剪框放置到最大】
        switch (touchPos) {
            case POS_CENTER:
                mBorderBound.left += deltaX;
                // fix border position
                if (mBorderBound.left < mBmpBound.left) {
                    mBorderBound.left = mBmpBound.left;
                }

                if ((mBorderBound.left > mBmpBound.right - mBorderWidth) || (mBorderBound.left == mBmpBound.right - mBorderWidth)) {
                    mBorderBound.left = mBmpBound.right - mBorderWidth;
                }


                mBorderBound.top += deltaY;
                if (mBorderBound.top < mBmpBound.top)
                    mBorderBound.top = mBmpBound.top;

                if (mBorderBound.top > mBmpBound.bottom - mBorderHeight)
                    mBorderBound.top = mBmpBound.bottom - mBorderHeight;

                mBorderBound.right = mBorderBound.left + mBorderWidth;
                mBorderBound.bottom = mBorderBound.top + mBorderHeight;

                break;

            case POS_TOP:
                resetTop(deltaY);
                break;
            case POS_BOTTOM:
                resetBottom(deltaY);
                break;
            case POS_LEFT:
                resetLeft(deltaX);
                break;
            case POS_RIGHT:
                resetRight(deltaX);
                break;
            case POS_TOP_LEFT:
                resetTop(deltaY);
                resetLeft(deltaX);
                break;
            case POS_TOP_RIGHT:
                resetTop(deltaY);
                resetRight(deltaX);
                break;
            case POS_BOTTOM_LEFT:
                resetBottom(deltaY);
                resetLeft(deltaX);
                break;
            case POS_BOTTOM_RIGHT:
                resetBottom(deltaY);
                resetRight(deltaX);
                break;
            default:

                break;
        }
        invalidate();
    }

    private void onActionUp(float x, float y) {

    }

    private int detectTouchPosition(float x, float y) {
        if (x > mBorderBound.left + TOUCH_FIELD && x < mBorderBound.right - TOUCH_FIELD
                && y > mBorderBound.top + TOUCH_FIELD && y < mBorderBound.bottom - TOUCH_FIELD)
            return POS_CENTER;

        if (x > mBorderBound.left + BORDER_CORNER_LENGTH && x < mBorderBound.right - BORDER_CORNER_LENGTH) {
            if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + TOUCH_FIELD)
                return POS_TOP;
            if (y > mBorderBound.bottom - TOUCH_FIELD && y < mBorderBound.bottom + TOUCH_FIELD)
                return POS_BOTTOM;
        }

        if (y > mBorderBound.top + BORDER_CORNER_LENGTH && y < mBorderBound.bottom - BORDER_CORNER_LENGTH) {
            if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + TOUCH_FIELD)
                return POS_LEFT;
            if (x > mBorderBound.right - TOUCH_FIELD && x < mBorderBound.right + TOUCH_FIELD)
                return POS_RIGHT;
        }

        // 前面的逻辑已经排除掉了几种情况 所以后面的 ┏ ┓ ┗ ┛ 边角就按照所占区域的方形来判断就可以了
        if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + BORDER_CORNER_LENGTH) {
            if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)
                return POS_TOP_LEFT;
            if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)
                return POS_BOTTOM_LEFT;
        }

        if (x > mBorderBound.right - BORDER_CORNER_LENGTH && x < mBorderBound.right + TOUCH_FIELD) {
            if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)
                return POS_TOP_RIGHT;
            if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)
                return POS_BOTTOM_RIGHT;
        }

        return -1;
    }

    private void setLastPosition(MotionEvent event) {
        mLastPoint.x = event.getX();
        mLastPoint.y = event.getY();
    }

    private void getBorderEdgeLength() {
        mBorderWidth = mBorderBound.width();
        mBorderHeight = mBorderBound.height();

    }
//接下来,这里的设置可以让裁剪框拉伸到最大而且不报错哦
    /**
     * 这里设置的是mBorderWidth(裁剪框的宽度),但是会出现将裁剪框左右拉到最大的时候,会出现裁剪框上的白边有些超出图片,所以会出现mBorderWidth>mBmpBound.width而报错,
     * 所以设置一下的if条件来设置当左右拉伸为最大时,裁剪框的宽度和图片宽度一致
     */

    private void getBorderEdgeWidth() {
        mBorderWidth = mBorderBound.width();
        if (mBorderWidth > mBmpBound.width()) {
            mBorderWidth = mBmpBound.width();
        }
    }
    /**
     * 这里设置的是mBorderHeight(裁剪框的高度),但是会出现将裁剪框上下拉到最大的时候,会出现裁剪框上的白边有些超出图片,所以会出现mBorderHeight>mBmpBound.height()而报错,
     * 所以设置一下的if条件来设置当上下拉伸为最大时,裁剪框的高度和图片高度一致
     */
    private void getBorderEdgeHeight() {
        mBorderHeight = mBorderBound.height();
        if (mBorderHeight > mBmpBound.height()) {
            mBorderHeight = mBmpBound.height();
        }
    }

    private void resetLeft(float delta) {
        mBorderBound.left += delta;
        getBorderEdgeWidth();
        fixBorderLeft();
    }

    private void resetTop(float delta) {
        mBorderBound.top += delta;
        getBorderEdgeHeight();
        fixBorderTop();
    }

    private void resetRight(float delta) {
        //这句话,如果注销,就不能够左右伸缩裁剪框,只能上下伸缩
        mBorderBound.right += delta;
        getBorderEdgeWidth();
        fixBorderRight();

    }

    private void resetBottom(float delta) {
        //这句话,如果注销,就不能够上下伸缩裁剪框,只能左右伸缩
        mBorderBound.bottom += delta;
        getBorderEdgeHeight();
        fixBorderBottom();
    }

    private void fixBorderLeft() {
        // fix left
        if (mBorderBound.left < mBmpBound.left)
            mBorderBound.left = mBmpBound.left;
        if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.left = mBorderBound.right - 2 * BORDER_CORNER_LENGTH;
    }

    private void fixBorderTop() {
        // fix top
        if (mBorderBound.top < mBmpBound.top)
            mBorderBound.top = mBmpBound.top;
        if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.top = mBorderBound.bottom - 2 * BORDER_CORNER_LENGTH;
    }

    private void fixBorderRight() {
        // fix right
        if (mBorderBound.right > mBmpBound.right) {

            mBorderBound.right = mBmpBound.right;
        }

        if (mBorderWidth < 2 * BORDER_CORNER_LENGTH) {
            mBorderBound.right = mBorderBound.left + 2 * BORDER_CORNER_LENGTH;
        }

    }

    private void fixBorderBottom() {
        // fix bottom
        if (mBorderBound.bottom > mBmpBound.bottom)
            mBorderBound.bottom = mBmpBound.bottom;
        if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.bottom = mBorderBound.top + 2 * BORDER_CORNER_LENGTH;
    }
}

最终得到裁剪图片

//将图片路径传进去
 myCropView.setBmpPath(path);


//得到的是裁剪后的图片
 Bitmap croppedImage = myCropView.getCroppedImage();


//大家记得在得到裁剪图片的那个方法里面写上这段代码哦
 sv = (ScrollView) findViewById(R.id.scrollview);

   //移动或变化 裁剪窗口  和  scrollview不冲突,这个写在onCreate方法里面的哦
        sv.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                myCropView.getParent().requestDisallowInterceptTouchEvent(false);
                return false;
            }
        });

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

附上两张图:

这个就是刚运行起来的样子哦~
这里写图片描述

这个就是拉伸到最大的样子,用原博主的会报异常哦,但是经过我的修改,现在不会啦
这里写图片描述

哈哈哈,大家可能发现了,怎么还是有一部分代码没有写注释呢,额。。这个嘛,因为本人比较懒而且笨哦,所以只注释了我需要的,其他的代码嘛,就有劳各位聪明人去明白啦~
哦~,对了,大家可以看看这个来帮助理解canvas
http://blog.csdn.net/iispring/article/details/49770651#comments

撤啦,bye~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值