Android 图片裁剪及保存

原创 2016年08月31日 15:40:35

最近项目中有个需求,就是进行图片的裁剪。
裁剪分为两种方式:1.矩形框裁剪 2.手势裁剪
在手势裁剪的过程中遇到一个问题,就是图片裁剪之后,背景不是透明的,下面给出我的解决方案。

@SuppressLint("DrawAllocation")
public class CropPictureView extends ImageView {
    private float density;
    private Paint mPaint;
    private Path mCirclePath;
    private Paint mImagePaint;
    private Path mFreehandPath;

    private Paint mPaintBitmap;

    private final Matrix mCircleatrix = new Matrix();
    // 放大镜的半径
    private static int RADIUS = 160;
    // 放大倍数
    private static final int FACTOR = 1;
    // 裁剪保留的bitmap
    private List<Bitmap> bitmaps = new ArrayList<Bitmap>();
    /**
     * 一次剪切手势动作
     */
    private boolean isTouchArea = false;

    private int mViewWidth = 0;
    private int mViewHeight = 0;
    private float scale = 1.0f;
    private float diff = 0.0f;

    private Bitmap sourceBitmap = null;

    public enum ViewType {
        RECTTYPE, PATHTYPE
    }

    private ViewType mViewType = ViewType.RECTTYPE;

    public void setmViewType(ViewType mViewType) {
        this.mViewType = mViewType;
    }

    public CropPictureView(Context context) {
        super(context);

        init();
    }

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

    public CropPictureView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        density = getContext().getResources().getDisplayMetrics().density;
        RADIUS = dipToPx(160);
        diff = diff;
        setFocusable(true);
        DRAW_STATUS = 0;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(diff);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.RED);

        mImagePaint = new Paint();
        mImagePaint.setAntiAlias(true);
        mImagePaint.setStrokeWidth(diff);
        mImagePaint.setStyle(Paint.Style.STROKE);
        mImagePaint.setColor(Color.BLACK);

        mCirclePath = new Path();
        mCirclePath.addRect(diff, diff, RADIUS + diff, RADIUS + diff, Direction.CW);
        // mCirclePath.addCircle(RADIUS, RADIUS, RADIUS, Direction.CW);
        mCircleatrix.setScale(FACTOR, FACTOR);
        mFrameRect = new RectF();
        mFreehandPath = new Path();

        mPaintBitmap = new Paint();
        mPaintBitmap.setFilterBitmap(true);
    }

    private Bitmap bm;

    // 重写该方法,在这里绘图
    @SuppressLint({ "DrawAllocation", "NewApi" })
    @Override
    protected void onDraw(Canvas mcanvas) {
        // super.onDraw(canvas);
        if (getDrawable() == null) {
            return;
        }
        // 生成画布图像
        bm = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bm);// 使用空白图片生成canvas

        canvas.save();
        canvas.translate(mImageRect.left, mImageRect.top);
        canvas.drawBitmap(getBitmap(), mCircleatrix, mPaintBitmap);
        canvas.restore();

        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG));
        canvas.drawRect(mImageRect, mImagePaint);
        if (DRAW_STATUS == 2) {
            if (!isTouchArea) {
                return;
            }
            if (mViewType == ViewType.RECTTYPE) {

                drawRect(canvas);
                move(canvas);
                drawRect(canvas);
            } else {
                canvas.drawPath(mFreehandPath, mPaint);
                move(canvas);
                canvas.drawPath(mFreehandPath, mPaint);
            }
        } else if (DRAW_STATUS == 3) {
            if (mViewType == ViewType.PATHTYPE) {
                mFreehandPath.close();
                mFrameRect = new RectF();
                mFreehandPath.computeBounds(mFrameRect, true);
            }
            // 如果画的矩形太小就不进行剪切
            if (Math.hypot(mFrameRect.width(), mFrameRect.height()) < 50) {
                isTouchArea = false;
            } else {
                if (mViewType == ViewType.PATHTYPE) {
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.DST_IN);
                    canvas.clipPath(mFreehandPath);
                    canvas.translate(mImageRect.left, mImageRect.top);
                    PaintFlagsDrawFilter dfd = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.FILTER_BITMAP_FLAG);
                    canvas.setDrawFilter(dfd);
                    canvas.drawBitmap(getBitmap(), mCircleatrix, null);
                }
                up();
                return;
            }
        } else if (DRAW_STATUS == 0) {

        } else if (DRAW_STATUS == 1) {
        }
        mcanvas.drawBitmap(bm, 0, 0, mPaintBitmap);
    }

    private void move(Canvas canvas) {
        // 剪切
        canvas.clipPath(mCirclePath);
        // 画放大后的图
        canvas.translate(RADIUS / 2 - endX * FACTOR + mImageRect.left, RADIUS / 2 - endY * FACTOR + mImageRect.top);
        canvas.drawBitmap(getBitmap(), mCircleatrix, null);
        canvas.translate(-mImageRect.left, -mImageRect.top);
        canvas.drawRect(mImageRect, mImagePaint);
    }

    /**
     * dip 转换成px
     * 
     * @param dip
     * @return
     */
    private int dipToPx(float dip) {

        return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
    }

    /**
     * 一次裁剪动作完成
     */
    public void up() {
        DRAW_STATUS = 0;
        Bitmap tempBitmap = getCropImage();
        bitmaps.add(tempBitmap);
        setImageBitmap(tempBitmap);
    }

    float startX = 0;
    float startY = 0;
    float endX = 0;
    float endY = 0;
    public static int DRAW_STATUS = 0;// 0,初始状态、1点击状态、2移动状态、3抬起状态
    private RectF mFrameRect = new RectF();
    private RectF mImageRect = new RectF();

    /**
     * 初始化边框
     */
    public void initRect() {
        startX = 0;
        startY = 0;
        endX = 0;
        endY = 0;
        mFrameRect = new RectF();
        if (mFreehandPath != null) {
            mFreehandPath.reset();
        } else {
            mFreehandPath = new Path();
        }
    }

    /**
     * 画矩形边框
     * 
     * @param canvas
     */
    public void drawRect(Canvas canvas) {
        if (endX > startX) {
            mFrameRect.left = (int) startX;
            mFrameRect.right = (int) endX;
        } else {
            mFrameRect.left = (int) endX;
            mFrameRect.right = (int) startX;
        }
        if (endY > startY) {
            mFrameRect.top = (int) startY;
            mFrameRect.bottom = (int) endY;
        } else {
            mFrameRect.top = (int) endY;
            mFrameRect.bottom = (int) startY;
        }
        mFrameRect.setIntersect(mFrameRect, mImageRect);
        canvas.drawRect(mFrameRect, mPaint);

    }

    private Bitmap getCovertBitmap() {

        Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.RGB_565);
        Canvas canvas = new Canvas(tmpBitmap);
        Drawable tempDrawable = getDrawable();
        tempDrawable.setBounds(new Rect(Math.round(mImageRect.left), Math.round(mImageRect.top), Math.round(mImageRect.right), Math.round(mImageRect.bottom)));
        tempDrawable.draw(canvas);
        return tmpBitmap;
    }

    /**
     * 
     * @param bitmap
     * @param w
     * @param h
     * @return
     */
    public Bitmap resizeBitmap(Bitmap bitmap, int w, int h) {
        if (bitmap != null) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            int newWidth = w;
            int newHeight = h;
            float scaleWight = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            Matrix matrix = new Matrix();
            matrix.postScale(scaleWight, scaleHeight);
            return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

        } else {
            return null;
        }
    }

    private Bitmap getBitmap() {
        Bitmap bm = null;
        Drawable d = getDrawable();
        if (d != null && d instanceof BitmapDrawable)
            bm = ((BitmapDrawable) d).getBitmap();
        return bm;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        int antion = event.getAction();
        if (antion == MotionEvent.ACTION_CANCEL) {
            return true;
        }
        float touchX = event.getX();
        float touchY = event.getY();

        // 点击时
        if (antion == MotionEvent.ACTION_DOWN) {
            initRect();
            startX = touchX;
            startY = touchY;
            endX = touchX;
            endY = touchY;
            mFreehandPath.moveTo(touchX, touchY);
            DRAW_STATUS = 1;
            if (mImageRect.contains(startX, startY)) {
                isTouchArea = true;
            } else {
                isTouchArea = false;
            }
            invalidate();
            return true;

        }
        // 拖动时
        if (antion == MotionEvent.ACTION_MOVE) {
            if (mViewType == ViewType.PATHTYPE) {
                if (mImageRect.contains(touchX, touchY)) {
                    touchMove(event);
                    // mFreehandPath.lineTo(touchX, touchY);
                }
            }
            endX = touchX;
            endY = touchY;
            DRAW_STATUS = 2;
            invalidate();
            return true;
        }
        // 抬起时
        if (antion == MotionEvent.ACTION_UP) {
            endX = touchX;
            endY = touchY;
            DRAW_STATUS = 3;
            invalidate();
            return true;
        }
        return super.onTouchEvent(event);
    }

    // 手指在屏幕上滑动时调用
    private void touchMove(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();

        final float previousX = endX;
        final float previousY = endY;

        final float dx = Math.abs(x - previousX);
        final float dy = Math.abs(y - previousY);

        // 两点之间的距离大于等于3时,生成贝塞尔绘制曲线
        if (dx >= 3 || dy >= 3) {
            // 设置贝塞尔曲线的操作点为起点和终点的一半
            float cX = (x + previousX) / 2;
            float cY = (y + previousY) / 2;

            // 二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点
            mFreehandPath.quadTo(previousX, previousY, cX, cY);

            // 第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值
        }
    }

    // 进行图片的裁剪,所谓的裁剪就是根据Drawable的新的坐标在画布上创建一张新的图片
    private Bitmap getCropImage() {
        if (mFrameRect.width() <= 0 || mFrameRect.height() <= 0) {
            isTouchArea = false;
            return null;
        }

        // this has a error: java.lang.IllegalArgumentException: x + width must
        // be <=bitmap.width()
        int x = Math.round(mFrameRect.left);
        int y = Math.round(mFrameRect.top);
        int w = Math.round(mFrameRect.width());
        int h = Math.round(mFrameRect.height());

        if (x + w > bm.getWidth()) {
            w = bm.getWidth() - x;
        }
        if (y + h > bm.getHeight()) {
            h = bm.getHeight() - y;
        }

        return Bitmap.createBitmap(bm, x, y, w, h, null, true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        final int viewHeight = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(viewWidth, viewHeight);

        mViewWidth = viewWidth - getPaddingLeft() - getPaddingRight();
        mViewHeight = viewHeight - getPaddingTop() - getPaddingBottom();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (getDrawable() != null)
            setupLayout(mViewWidth, mViewHeight);
    }

    /**
     * 重新设置view的图片
     */
    public void setupLayout(int viewW, int viewH) {
        if (viewW == 0 || viewH == 0)
            return;
        float imgWidth = getDrawable().getIntrinsicWidth();
        float imgHight = getDrawable().getIntrinsicHeight();
        float viewRatio = (float) viewW / (float) viewH;
        float imgRatio = imgWidth / imgHight;
        if (imgRatio >= viewRatio) {
            scale = (float) viewW / imgWidth;
        } else if (imgRatio < viewRatio) {
            scale = (float) viewH / imgHight;
        }
        float w = imgWidth * scale;
        float h = imgHight * scale;
        float left = (viewW - w) / 2;
        float top = (viewH - h) / 2;
        float right = left + w;
        float bottom = top + h;
        mImageRect = new RectF(left, top, right, bottom);
        mCircleatrix.setScale(FACTOR * scale, FACTOR * scale);
    }

    public Bitmap getCropBitmap() {

        if (bitmaps != null && bitmaps.size() > 0) {
            return bitmaps.get(bitmaps.size() - 1);
        } else {
            return getBitmap();
        }
    }

    public void retroversion() {
        LogUtil.e("撤销tupain   = " + bitmaps.size());
        if (bitmaps != null && bitmaps.size() > 1) {
            setImageBitmap(bitmaps.get(bitmaps.size() - 2));
            invalidate();
            bitmaps.remove(bitmaps.size() - 1);
        }

        if (bitmaps.size() == 0) {
            bitmaps.add(sourceBitmap);
        }

    }

    /**
     * Set source image bitmap
     *
     * @param bitmap
     *            src image bitmap
     */
    @Override
    public void setImageBitmap(Bitmap bitmap) {
        super.setImageBitmap(bitmap); // calles setImageDrawable internally
        if (sourceBitmap == null) {
            sourceBitmap = bitmap;
        }
        if (sourceBitmap != null && bitmaps.size() == 0) {
            bitmaps.add(sourceBitmap);
        }
    }

    /**
     * Set source image resource id
     *
     * @param resId
     *            source image resource id
     */
    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        updateLayout();
    }

    /**
     * Set image drawable.
     *
     * @param drawable
     *            source image drawable
     */
    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        updateLayout();
    }

    /**
     * Set image uri
     *
     * @param uri
     *            source image local uri
     */
    @Override
    public void setImageURI(Uri uri) {
        super.setImageURI(uri);
        updateLayout();
    }

    private void updateLayout() {
        Drawable d = getDrawable();
        if (d != null) {
            setupLayout(mViewWidth, mViewHeight);
        }
    }

    private Bitmap getTransBitmap(Bitmap bm) {
        int[] pix = new int[bm.getWidth() * bm.getHeight()];

        for (int y = 0; y < bm.getHeight(); y++)
            for (int x = 0; x < bm.getWidth(); x++) {
                int index = y * bm.getWidth() + x;
                int r = ((pix[index] >> 16) & 0xff) | 0xff;
                int g = ((pix[index] >> 8) & 0xff) | 0xff;
                int b = (pix[index] & 0xff) | 0xff;
                pix[index] = 0xff000000 | (r << 16) | (g << 8) | b;

            }
        bm.setPixels(pix, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
        return Bitmap.createBitmap(bm);
    }
}

(代码比较乱)
上面分为两种裁剪方式:RECTTYPE–矩形, PATHTYPE–手势路径。
解决背景不是透明的方式:

if (mViewType == ViewType.PATHTYPE) {
                    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.DST_IN);
                    canvas.clipPath(mFreehandPath);
                    canvas.translate(mImageRect.left, mImageRect.top);
                    PaintFlagsDrawFilter dfd = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.FILTER_BITMAP_FLAG);
                    canvas.setDrawFilter(dfd);
                    canvas.drawBitmap(getBitmap(), mCircleatrix, null);
                }

canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.DST_IN);
黑色粗体位置就是设置裁剪的背景颜色和层叠方式的,PorterDuff.Mode的具体方式如下:
从上面我们可以看到PorterDuff.Mode为枚举类,一共有16个枚举值:
1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN
取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN
取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY
取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN
取两图层全部区域,交集部分变为透明色

可以转到链接查看:http://blog.csdn.net/t12x3456/article/details/10432935

另外还有一个就是:图片保存到本地背景是黑色的,这里要将保存格式改为png:
* bm.compress(Bitmap.CompressFormat.PNG, 90, baos);*

public static InputStream bitmapToStream(Bitmap bm) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.PNG, 90, baos);
        InputStream is = new ByteArrayInputStream(baos.toByteArray());
        return is;
    }
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android大图片裁剪终极解决方案(中:从相册截图)

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-)  http://my.oschina.net/ryanhoo/blog/86853  ...

Android选择图片并裁剪,"无法保存经过裁剪的图片"

今天写一段到相册选择照片并裁剪的程序,遇到"无法保存经过裁剪的图片"的错误,网上找了很多,没找到满意的,下面一点点分析解决了这个问题。 首先,裁剪完照片后要保存到自己定义的目录里面,一定要保证这个目...

Android 调用系统相机、图库 ,取图片并裁剪、保存sd卡

布局只有一个ImageView就不给出了 package com.example.setimage; import java.io.File; import java.io.FileInputStr...

android 7.0以上裁剪和选择sd卡外面的图片适配(日记)

今天适配一下 7.0的 拍照 选择照片,裁剪,的时候按照网上说的来做,发现拍照可以,选择图片也可以,但是就是裁剪的时候不行,手机会弹出:无法保存经过裁剪的图片 后来才发现,在设置裁剪要保存的 inte...

Android 7.0 适配相机及裁剪图片

Android 7.0调用相机及裁剪图片,还有动态权限。 其中相机调用正常通过,但是裁剪就不是了,这次重点就是裁剪EXTRA_OUTPUT, 别用 FileProvider.getUriForFile...

史上最强Android 开启照相或者是从本地相册选中一张图片以后先裁剪在保存并显示的讲解附源码i

整个程序的布局很简单 只在一个垂直方向上的线性布局里面有俩个按钮(Button)和一个显示图片的控件(ImageView) 这里就不给出这部分的代码了 1.是打开系统的相册 Intent alb...

android Rect的使用

转:http://byandby.javaeye.com/blog/826230 Java代码  1.   //绘制矩形   2.   canvas.drawRect(new...

android Drawbitmap 画一个图片(Rect 的作用)

Rect src = new Rect();// 图片       Rect dst = new Rect();// 屏幕       src.left = bx;       src.top ...

如何实现EditText的历史记录功能(关闭后再次打开显示上次的记录)?

内容:主要是实现从主Activity跳转到设置Activity,然后点击EditText进行参数设置(这里均是Number类型),参数需要限制类型、限制长度,同时在退出当前Activity或者关闭应用...

Android软键盘输入详解

IM(Input Method): 输入法。是指通过键盘等输入设备输入 输入设备上没有 的字符的 方法/程序/处理器 。最开始是特指在拉丁字母键盘上输入CJK (Chinese, Japanese a...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 图片裁剪及保存
举报原因:
原因补充:

(最多只允许输入30个字)