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