Android 图片裁剪及保存
来源:互联网 发布:php unpack函数 编辑:程序博客网 时间:2024/04/28 16:39
最近项目中有个需求,就是进行图片的裁剪。
裁剪分为两种方式: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 图片裁剪及保存
- android 无法保存裁剪图片
- Android拍照压缩裁剪并保存图片
- Android图片选择及裁剪
- actionscript3.0 图片裁剪及保存jpg详解
- Android 可变裁剪区及缩放裁剪图片
- Android拍照及相册图片裁剪操作
- Android选择图片并裁剪,"无法保存经过裁剪的图片"
- Android——最简单的图片裁剪 调用系统原生裁剪图片(+保存不成功)
- android 裁剪涂抹区域并保存成图片
- 使用Android系统调用裁剪图片并保存
- OpenCV裁剪图片并保存
- OpenCV裁剪图片并保存
- android拍照获得图片及获得图片后并裁剪
- android系统图片裁剪
- android裁剪图片
- android 图片裁剪
- android 裁剪图片
- thrift 获取客户端client ip TBinaryProtocol TFramedTransport TNonblockingServerSocket
- Android中的进程简介(可见进程,前台进程,后台进程等)
- 关于SMO中b值的计算
- JavaScript运算符中的隐式转换规律
- 白话p-value
- Android 图片裁剪及保存
- JSP页面顶端出现“红色”的报错信息:The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build
- mysql远程授权登陆的问题
- android通过高德地图仿微信发送地址移动地图自动搜索附近店铺及地址(架包和配置参考高德地图官网)
- 2016/8/31 刷题记录
- centos 7 root用户,在/root下建的文件都跑桌面了,是怎么回事
- HDU 2047 阿牛的EOF牛肉串
- 高并发量网站解决方案 (转)
- BZOJ2214: [Poi2011]Shift