裁剪效果
直接上代码:
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.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
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;
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);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
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:
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;
}
@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() {
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);
tmpBitmap.recycle();
return ret;
}
public int dipToPx(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
public class FloatDrawable extends Drawable {
private Context mContext;
private int offset = 50;
private Paint mLinePaint = new Paint();
private Paint mLinePaint2 = new Paint();
{
mLinePaint.setARGB(200, 50, 50, 50);
mLinePaint.setStrokeWidth(3F);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.parseColor("#3388FF"));
//
mLinePaint2.setARGB(200, 50, 50, 50);
mLinePaint2.setStrokeWidth(8F);
mLinePaint2.setStyle(Paint.Style.STROKE);
mLinePaint2.setAntiAlias(true);
mLinePaint2.setColor(Color.parseColor("#3388FF"));
}
public FloatDrawable(Context context) {
super();
this.mContext = context;
}
public int getBorderWidth() {
return dipToPx(mContext, offset);//根据dip计算的像素值,做适配用的
}
public int getBorderHeight() {
return dipToPx(mContext, offset);
}
@Override
public void draw(Canvas canvas) {
int left = getBounds().left;
int top = getBounds().top;
int right = getBounds().right;
int bottom = getBounds().bottom;
Rect mRect = new Rect(left + dipToPx(mContext, offset) / 2+3,
top + dipToPx(mContext, offset) / 2+3,
right - dipToPx(mContext, offset) / 2-3,
bottom - dipToPx(mContext, offset) / 2-3);
//画默认的选择框
canvas.drawRect(mRect, mLinePaint);
//画四个角的四个粗拐角、也就是八条粗线
canvas.drawLine((left + dipToPx(mContext, offset) / 2 - 3.5f),
top + dipToPx(mContext, offset) / 2+2,
left + dipToPx(mContext, offset) - 8f,
top + dipToPx(mContext, offset) / 2+2, mLinePaint2);
canvas.drawLine(left + dipToPx(mContext, offset) / 2+2,
top + dipToPx(mContext, offset) / 2,
left + dipToPx(mContext, offset) / 2+2,
top + dipToPx(mContext, offset) / 2 + 30, mLinePaint2);
canvas.drawLine(right - dipToPx(mContext, offset) + 8f,
top + dipToPx(mContext, offset) / 2+2,
right - dipToPx(mContext, offset) / 2,
top + dipToPx(mContext, offset) / 2+2, mLinePaint2);
canvas.drawLine(right - dipToPx(mContext, offset) / 2-2,
top + dipToPx(mContext, offset) / 2 - 3.5f,
right - dipToPx(mContext, offset) / 2-2,
top + dipToPx(mContext, offset) / 2 + 30, mLinePaint2);
canvas.drawLine((left + dipToPx(mContext, offset) / 2 - 3.5f),
bottom - dipToPx(mContext, offset) / 2-2,
left + dipToPx(mContext, offset) - 8f,
bottom - dipToPx(mContext, offset) / 2-2, mLinePaint2);
canvas.drawLine((left + dipToPx(mContext, offset) / 2+2),
bottom - dipToPx(mContext, offset) / 2,
left + dipToPx(mContext, offset) / 2+2,
bottom - dipToPx(mContext, offset) / 2 - 30f, mLinePaint2);
canvas.drawLine((right - dipToPx(mContext, offset) + 8f),
bottom - dipToPx(mContext, offset) / 2-2,
right - dipToPx(mContext, offset) / 2,
bottom - dipToPx(mContext, offset) / 2-2, mLinePaint2);
canvas.drawLine((right - dipToPx(mContext, offset) / 2-2),
bottom - dipToPx(mContext, offset) / 2 - 30f,
right - dipToPx(mContext, offset) / 2-2,
bottom - dipToPx(mContext, offset) / 2 + 3.5f, 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));
}
@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);
}
}
用法
private fun initData() {
var data = BitmapHelper.getLogoBitmap(imgName) //获取对应的bitmap
if (data == null) {
ToastUtils.showToast("数据异常,请重试")
return
}
val num =
data.width.toFloat() / (DensityUtil.getPhoneWidth(this@CropBrandActivity) * 0.8)
val params = mBinding.cropimage.layoutParams
if (data.width < DensityUtil.getPhoneWidth(this@CropBrandActivity) * 0.5 || data.height < 100) {
val bei =
(DensityUtil.getPhoneWidth(this@CropBrandActivity) * 0.8) / data.width.toFloat()
data = Bitmap.createScaledBitmap(
data, (DensityUtil.getPhoneWidth(this@CropBrandActivity) * 0.8).toInt(),
(data.height * bei).toInt(), true
)
params.height = (data.height * bei).toInt()
params.width = (DensityUtil.getPhoneWidth(this@CropBrandActivity) * 0.8).toInt()
} else {
params.height = (data.height.toFloat() / num).toInt()
params.width = (DensityUtil.getPhoneWidth(this@CropBrandActivity) * 0.8).toInt()
}
mBinding.cropimage.layoutParams = params
mBinding.cropimage.setDrawable(data, data.width, data.height)
}