相框移动,四个角可以拖动,随便剪接
自定义类
package com.diction.app.android._av7.view.crop_iamge; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.diction.app.android._av7._view.utils.PrintUtilsJava; public class CropImageView extends View { // 在touch重要用到的点, private float mX_1 = 0; private float mY_1 = 0; // 触摸事件判断 private final int STATUS_SINGLE = 1; private final int STATUS_MULTI_START = 2; private final int STATUS_MULTI_TOUCHING = 3; // 当前状态 private int mStatus = STATUS_SINGLE; // 默认裁剪的宽高 private int cropWidth; private int cropHeight; // 浮层Drawable的四个点 private final int EDGE_LT = 1; private final int EDGE_RT = 2; private final int EDGE_LB = 3; private final int EDGE_RB = 4; private final int EDGE_MOVE_IN = 5; private final int EDGE_MOVE_OUT = 6; private final int EDGE_NONE = 7; public int currentEdge = EDGE_NONE; protected float oriRationWH = 0; protected Drawable mDrawable; protected FloatDrawable mFloatDrawable; protected Rect mDrawableSrc = new Rect();// 图片Rect变换时的Rect protected Rect mDrawableDst = new Rect();// 图片Rect protected Rect mDrawableFloat = new Rect();// 浮层的Rect protected boolean isFirst = true; private boolean isTouchInSquare = true; protected Context mContext; private Bitmap mBitmap; private boolean mcanMove = true; public void setCanMove(boolean canMove) { this.mcanMove = canMove; PrintUtilsJava.pringtLog("setCanMove---->"+ canMove); } public CropImageView(Context context) { super(context); init(context); } public CropImageView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public CropImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } @SuppressLint("NewApi") private void init(Context context) { this.mContext = context; try { if (android.os.Build.VERSION.SDK_INT >= 11) { this.setLayerType(LAYER_TYPE_SOFTWARE, null); } } catch (Exception e) { e.printStackTrace(); } mFloatDrawable = new FloatDrawable(context); } public void setDrawable(Bitmap bitmap, int cropWidth, int cropHeight) { mBitmap = bitmap; this.mDrawable = new BitmapDrawable(bitmap); this.cropWidth = cropWidth; this.cropHeight = cropHeight; this.isFirst = true; invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (mBitmap != null) { if ((mBitmap.getHeight() > heightSize) && (mBitmap.getHeight() > mBitmap.getWidth())) { widthSize = heightSize * mBitmap.getWidth() / mBitmap.getHeight(); } else if ((mBitmap.getWidth() > widthSize) && (mBitmap.getWidth() > mBitmap.getHeight())) { heightSize = widthSize * mBitmap.getHeight() / mBitmap.getWidth(); } else { heightSize = mBitmap.getHeight(); widthSize = mBitmap.getWidth(); } } setMeasuredDimension(widthSize, heightSize); } private boolean hasMover = false; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { if (!mcanMove){ return false; } if (event.getPointerCount() > 1) { if (mStatus == STATUS_SINGLE) { mStatus = STATUS_MULTI_START; } else if (mStatus == STATUS_MULTI_START) { mStatus = STATUS_MULTI_TOUCHING; } } else { if (mStatus == STATUS_MULTI_START || mStatus == STATUS_MULTI_TOUCHING) { mX_1 = event.getX(); mY_1 = event.getY(); } mStatus = STATUS_SINGLE; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mX_1 = event.getX(); mY_1 = event.getY(); currentEdge = getTouch((int) mX_1, (int) mY_1); break; case MotionEvent.ACTION_UP: if (hasMover ){ if(moverListner != null){ moverListner.onMoverListner(); } } hasMover = false; break; case MotionEvent.ACTION_POINTER_UP: currentEdge = EDGE_NONE; break; case MotionEvent.ACTION_MOVE: if (mStatus == STATUS_MULTI_TOUCHING) { } else if (mStatus == STATUS_SINGLE) { int dx = (int) (event.getX() - mX_1); int dy = (int) (event.getY() - mY_1); mX_1 = event.getX(); mY_1 = event.getY(); // TODO 如果手指坐标超出view范围,则返回。 // 理论上应该写在这里,但会产生一个移动四个角过快的时候不容易移到到边界的现象 // if (mX_1 > getWidth() || mX_1 < 0 || mY_1 > getHeight() || mY_1 < 0) { // break; // } // 根據得到的那一个角,并且变换Rect if (!(dx == 0 && dy == 0)) { switch (currentEdge) { case EDGE_LT: hasMover = true; int left = mDrawableFloat.left +dx; int right = mDrawableFloat.right; int top = mDrawableFloat.top+dy; int bottom = mDrawableFloat.bottom; Log.e("mover_","left -->"+left +" right = "+ right + " top = "+ top+" bottom= "+ bottom); if (right -left<90){ right= left+90; } if (bottom- top<90){ bottom = top+90; } mDrawableFloat.set(left,top,right,bottom); /* mDrawableFloat.set(mDrawableFloat.left + dx, mDrawableFloat.top + dy, mDrawableFloat.right, mDrawableFloat.bottom);*/ break; case EDGE_RT: hasMover = true; int leftR = mDrawableFloat.left ; int rightR = mDrawableFloat.right+dx; int topR = mDrawableFloat.top+dy; int bottomR = mDrawableFloat.bottom; if (rightR -leftR<90){ rightR= leftR+90; } if (bottomR- topR<90){ bottomR = topR+90; } mDrawableFloat.set(leftR,topR,rightR,bottomR); /* mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top + dy, mDrawableFloat.right + dx, mDrawableFloat.bottom);*/ break; case EDGE_LB: hasMover = true; int leftLB = mDrawableFloat.left +dx; int rightLB = mDrawableFloat.right; int topLB = mDrawableFloat.top; int bottomLB = mDrawableFloat.bottom +dy; if (rightLB -leftLB<90){ rightLB= leftLB+90; } if (bottomLB- topLB<90){ bottomLB = topLB+90; } mDrawableFloat.set(leftLB,topLB,rightLB,bottomLB); /* mDrawableFloat.set(mDrawableFloat.left + dx, mDrawableFloat.top, mDrawableFloat.right, mDrawableFloat.bottom + dy);*/ break; case EDGE_RB: hasMover = true; int leftRB = mDrawableFloat.left ; int rightRB = mDrawableFloat.right+dx; int topRB = mDrawableFloat.top; int bottomRB = mDrawableFloat.bottom +dy; if (rightRB -leftRB<90){ rightRB= leftRB+90; } if (bottomRB- topRB<90){ bottomRB = topRB+90; } mDrawableFloat.set(leftRB,topRB,rightRB,bottomRB); /* mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.right + dx, mDrawableFloat.bottom + dy);*/ break; case EDGE_MOVE_IN: hasMover = true; // 因为手指一直在移动,应该实时判断是否超出裁剪框(手指移动到图片范围外) isTouchInSquare = mDrawableFloat.contains((int) event.getX(), (int) event.getY()); if (isTouchInSquare) { mDrawableFloat.offset(dx, dy); } break; case EDGE_MOVE_OUT: break; } mDrawableFloat.sort(); invalidate(); } } break; } /* switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mX_1 = event.getX(); mY_1 = event.getY(); currentEdge = getTouch((int) mX_1, (int) mY_1); break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_POINTER_UP: currentEdge = EDGE_NONE; break; case MotionEvent.ACTION_MOVE: if (mStatus == STATUS_MULTI_TOUCHING) { } else if (mStatus == STATUS_SINGLE) { int dx = (int) (event.getX() - mX_1); int dy = (int) (event.getY() - mY_1); mX_1 = event.getX(); mY_1 = event.getY(); // TODO 如果手指坐标超出view范围,则返回。 // 理论上应该写在这里,但会产生一个移动四个角过快的时候不容易移到到边界的现象 // if (mX_1 > getWidth() || mX_1 < 0 || mY_1 > getHeight() || mY_1 < 0) { // break; // } // 根據得到的那一个角,并且变换Rect if (!(dx == 0 && dy == 0)) { switch (currentEdge) { case EDGE_LT: mDrawableFloat.set(mDrawableFloat.left + dx, mDrawableFloat.top + dy, mDrawableFloat.right, mDrawableFloat.bottom); break; case EDGE_RT: mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top + dy, mDrawableFloat.right + dx, mDrawableFloat.bottom); break; case EDGE_LB: mDrawableFloat.set(mDrawableFloat.left + dx, mDrawableFloat.top, mDrawableFloat.right, mDrawableFloat.bottom + dy); break; case EDGE_RB: mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.right + dx, mDrawableFloat.bottom + dy); break; case EDGE_MOVE_IN: // 因为手指一直在移动,应该实时判断是否超出裁剪框(手指移动到图片范围外) isTouchInSquare = mDrawableFloat.contains((int) event.getX(), (int) event.getY()); if (isTouchInSquare) { mDrawableFloat.offset(dx, dy); } break; case EDGE_MOVE_OUT: break; } mDrawableFloat.sort(); invalidate(); } } break; }*/ return true; } // 根据初触摸点判断是触摸的Rect哪一个角 public int getTouch(int eventX, int eventY) { Rect mFloatDrawableRect = mFloatDrawable.getBounds(); int mFloatDrawableWidth = mFloatDrawable.getBorderWidth(); int mFloatDrawableHeight = mFloatDrawable.getBorderHeight(); if (mFloatDrawableRect.left <= eventX && eventX < (mFloatDrawableRect.left + mFloatDrawableWidth) && mFloatDrawableRect.top <= eventY && eventY < (mFloatDrawableRect.top + mFloatDrawableHeight)) { return EDGE_LT; } else if ((mFloatDrawableRect.right - mFloatDrawableWidth) <= eventX && eventX < mFloatDrawableRect.right && mFloatDrawableRect.top <= eventY && eventY < (mFloatDrawableRect.top + mFloatDrawableHeight)) { return EDGE_RT; } else if (mFloatDrawableRect.left <= eventX && eventX < (mFloatDrawableRect.left + mFloatDrawableWidth) && (mFloatDrawableRect.bottom - mFloatDrawableHeight) <= eventY && eventY < mFloatDrawableRect.bottom) { return EDGE_LB; } else if ((mFloatDrawableRect.right - mFloatDrawableWidth) <= eventX && eventX < mFloatDrawableRect.right && (mFloatDrawableRect.bottom - mFloatDrawableHeight) <= eventY && eventY < mFloatDrawableRect.bottom) { return EDGE_RB; } else if (mFloatDrawableRect.contains(eventX, eventY)) { return EDGE_MOVE_IN; } return EDGE_MOVE_OUT; } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onDraw(Canvas canvas) { if (mDrawable == null) { return; } if (mDrawable.getIntrinsicWidth() == 0 || mDrawable.getIntrinsicHeight() == 0) { return; } configureBounds(); // 在画布上画图片 mDrawable.draw(canvas); canvas.save(); // 在画布上画浮层FloatDrawable,Region.Op.DIFFERENCE是表示Rect交集的补集 canvas.clipRect(mDrawableFloat, Region.Op.DIFFERENCE); // 在交集的补集上画上灰色用来区分 canvas.drawColor(Color.parseColor("#a0000000")); canvas.restore(); // 画浮层 mFloatDrawable.draw(canvas); } protected void configureBounds() { // configureBounds在onDraw方法中调用 // isFirst的目的是下面对mDrawableSrc和mDrawableFloat只初始化一次, // 之后的变化是根据touch事件来变化的,而不是每次执行重新对mDrawableSrc和mDrawableFloat进行设置 if (isFirst) { oriRationWH = ((float) mDrawable.getIntrinsicWidth()) / ((float) mDrawable.getIntrinsicHeight()); final float scale = mContext.getResources().getDisplayMetrics().density; int mDrawableW = (int) (mDrawable.getIntrinsicWidth() * scale + 0.5f); if ((mDrawable.getIntrinsicHeight() * scale + 0.5f) > getHeight()) { mDrawableW = (int) ((mDrawable.getIntrinsicWidth() * scale + 0.5f) * (getHeight() / (mDrawable.getIntrinsicHeight() * scale + 0.5f))); } int w = Math.min(getWidth(), mDrawableW); int h = (int) (w / oriRationWH); int left = (getWidth() - w) / 2; int top = (getHeight() - h) / 2; int right = left + w; int bottom = top + h; mDrawableSrc.set(left, top, right, bottom); mDrawableDst.set(mDrawableSrc); int floatWidth = dipToPx(mContext, cropWidth); int floatHeight = dipToPx(mContext, cropHeight); if (floatWidth > getWidth()) { floatWidth = getWidth(); floatHeight = cropHeight * floatWidth / cropWidth; } if (floatHeight > getHeight()) { floatHeight = getHeight(); floatWidth = cropWidth * floatHeight / cropHeight; } int floatLeft = (getWidth() - floatWidth) / 2; int floatTop = (getHeight() - floatHeight) / 2; mDrawableFloat.set(floatLeft, floatTop, floatLeft + floatWidth, floatTop + floatHeight); isFirst = false; } else if (getTouch((int) mX_1, (int) mY_1) == EDGE_MOVE_IN) { if (mDrawableFloat.left < 0) { mDrawableFloat.right = mDrawableFloat.width(); mDrawableFloat.left = 0; } if (mDrawableFloat.top < 0) { mDrawableFloat.bottom = mDrawableFloat.height(); mDrawableFloat.top = 0; } if (mDrawableFloat.right > getWidth()) { mDrawableFloat.left = getWidth() - mDrawableFloat.width(); mDrawableFloat.right = getWidth(); } if (mDrawableFloat.bottom > getHeight()) { mDrawableFloat.top = getHeight() - mDrawableFloat.height(); mDrawableFloat.bottom = getHeight(); } mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.right, mDrawableFloat.bottom); } else { if (mDrawableFloat.left < 0) { mDrawableFloat.left = 0; } if (mDrawableFloat.top < 0) { mDrawableFloat.top = 0; } if (mDrawableFloat.right > getWidth()) { mDrawableFloat.right = getWidth(); mDrawableFloat.left = getWidth() - mDrawableFloat.width(); } if (mDrawableFloat.bottom > getHeight()) { mDrawableFloat.bottom = getHeight(); mDrawableFloat.top = getHeight() - mDrawableFloat.height(); } mDrawableFloat.set(mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.right, mDrawableFloat.bottom); } mDrawable.setBounds(mDrawableDst); mFloatDrawable.setBounds(mDrawableFloat); } // 进行图片的裁剪,所谓的裁剪就是根据Drawable的新的坐标在画布上创建一张新的图片 public Bitmap getCropImage() { if (moverListner != null && mDrawableFloat!= null){ moverListner.onImageCropcoordinate( mDrawableFloat.left,mDrawableFloat.top,mDrawableFloat.width(),mDrawableFloat.height()); } Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565); Canvas canvas = new Canvas(tmpBitmap); mDrawable.draw(canvas); Matrix matrix = new Matrix(); float scale = (float) (mDrawableSrc.width()) / (float) (mDrawableDst.width()); matrix.postScale(scale, scale); Bitmap ret = Bitmap.createBitmap(tmpBitmap, mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.width(), mDrawableFloat.height(), matrix, true); PrintUtilsJava.pringtLog("cropImage:002 mDrawableFloat.left :" + mDrawableFloat.left); PrintUtilsJava.pringtLog("cropImage:002 mDrawableFloat.top :" + mDrawableFloat.top); PrintUtilsJava.pringtLog("cropImage:002 mDrawableFloat.width :" + mDrawableFloat.width()); PrintUtilsJava.pringtLog("cropImage:002 mDrawableFloat.height() :" + mDrawableFloat.height()); tmpBitmap.recycle(); return ret; } public int dipToPx(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } public interface OnFloatDrawableMoverListner{ void onMoverListner(); void onImageCropcoordinate(int left,int top,int width,int height); } private OnFloatDrawableMoverListner moverListner; public void setOnFloatDrawableMoverListner(OnFloatDrawableMoverListner ll){ moverListner = ll; } }
浮动移动框
package com.diction.app.android._av7.view.crop_iamge; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.RequiresApi; public class FloatDrawable extends Drawable { private Context mContext; private int offset = 50; private Paint mLinePaint = new Paint(); private Paint mLinePaint2 = new Paint(); private Path path = new Path(); private int lineLength = 40; private int panintWidth = 0; private int mPaddings = 15; { //设置Path mLinePaint.setARGB(200, 50, 50, 50); mLinePaint.setStrokeWidth(1F); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setAntiAlias(true); mLinePaint.setColor(Color.WHITE); // mLinePaint2.setARGB(200, 50, 50, 50); mLinePaint2.setStrokeWidth(10F); panintWidth = (int) mLinePaint2.getStrokeWidth(); mLinePaint2.setStyle(Paint.Style.STROKE); mLinePaint2.setAntiAlias(true); mLinePaint2.setColor(Color.WHITE); } public FloatDrawable(Context context) { super(); this.mContext = context; } public int getBorderWidth() { return dipToPx(mContext, offset);//根据dip计算的像素值,做适配用的 } public int getBorderHeight() { return dipToPx(mContext, offset); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void draw(Canvas canvas) { int left = getBounds().left; int top = getBounds().top; int right = getBounds().right; int bottom = getBounds().bottom; // canvas.drawLine(left+ mPaddings,top,left+lineLength+mPaddings,top,mLinePaint2); canvas.drawLine(left,top+ mPaddings,left,top+lineLength+mPaddings,mLinePaint2); canvas.drawArc(left,top,left+ mPaddings *2,top+ mPaddings *2,180,90,false,mLinePaint2); // canvas.drawLine(right- mPaddings-lineLength ,top,right- mPaddings,top,mLinePaint2); //ok canvas.drawLine(right ,top+mPaddings,right,top+mPaddings+lineLength,mLinePaint2); //ok canvas.drawArc(right-mPaddings *2,top,right,top+ mPaddings *2,270,90,false,mLinePaint2); canvas.drawLine(left,bottom-lineLength-mPaddings,left,bottom-mPaddings,mLinePaint2); canvas.drawLine(left+mPaddings,bottom,left+mPaddings+lineLength,bottom,mLinePaint2); canvas.drawArc(left,bottom-mPaddings*2,left+mPaddings*2,bottom,90,90,false,mLinePaint2); canvas.drawLine(right-lineLength-mPaddings,bottom,right-mPaddings,bottom,mLinePaint2); canvas.drawLine(right,bottom-mPaddings,right,bottom-mPaddings-lineLength,mLinePaint2); canvas.drawArc(right-mPaddings*2,bottom-mPaddings*2,right,bottom,0,90,false,mLinePaint2); } @Override public void setBounds(Rect bounds) { // super.setBounds(new Rect(bounds.left - dipToPx(mContext, offset) / 2, // bounds.top - dipToPx(mContext, offset) / 2, // bounds.right + dipToPx(mContext, offset) / 2, // bounds.bottom + dipToPx(mContext, offset) / 2)); super.setBounds(new Rect(bounds.left , bounds.top, bounds.right, bounds.bottom)); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } @Override public int getOpacity() { return PixelFormat.UNKNOWN; } public int dipToPx(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } }
使用
cropimage.setDrawable(newBitmap, 300, 300)