Android 图片裁剪及保存

最近项目中有个需求,就是进行图片的裁剪。
裁剪分为两种方式: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;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值