自定义裁剪框View
public class ClipImageBorderView extends View
{
private int mHorizontalPadding;
private int mVerticalPadding;
private int mWidth;
private int mBorderColor = Color.parseColor("#FFFFFF");
private int mBorderWidth = 1;
private Paint mPaint;
public ClipImageBorderView(Context context)
{
this(context, null);
}
public ClipImageBorderView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public ClipImageBorderView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
mBorderWidth = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, mBorderWidth, getResources()
.getDisplayMetrics());
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
mWidth = getWidth() - 2 * mHorizontalPadding;
mVerticalPadding = (getHeight() - mWidth) / 2;
mPaint.setColor(Color.parseColor("#aa000000"));
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, mHorizontalPadding, getHeight(), mPaint);
canvas.drawRect(getWidth() - mHorizontalPadding, 0, getWidth(),
getHeight(), mPaint);
canvas.drawRect(mHorizontalPadding, 0, getWidth() - mHorizontalPadding,
mVerticalPadding, mPaint);
canvas.drawRect(mHorizontalPadding, getHeight() - mVerticalPadding,
getWidth() - mHorizontalPadding, getHeight(), mPaint);
mPaint.setColor(mBorderColor);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(mHorizontalPadding, mVerticalPadding, getWidth()
- mHorizontalPadding, getHeight() - mVerticalPadding, mPaint);
canvas.drawRect(mHorizontalPadding, mVerticalPadding+(getHeight() - mVerticalPadding*2)/3, getWidth()
- mHorizontalPadding, getHeight() - mVerticalPadding-(getHeight() - mVerticalPadding*2)/3, mPaint);
canvas.drawRect(mHorizontalPadding+(getWidth()-mHorizontalPadding)/3, mVerticalPadding, getWidth()
- mHorizontalPadding-(getWidth()-mHorizontalPadding)/3, getHeight() - mVerticalPadding, mPaint);
}
public void setHorizontalPadding(int mHorizontalPadding)
{
this.mHorizontalPadding = mHorizontalPadding;
}
}
自定义可放大,缩小的ImageView
public class ClipZoomImageView extends androidx.appcompat.widget.AppCompatImageView implements
ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener,
ViewTreeObserver.OnGlobalLayoutListener
{
private static final String TAG = ClipZoomImageView.class.getSimpleName();
public static float SCALE_MAX = 4.0f;
private static float SCALE_MID = 2.0f;
private float initScale = 1.0f;
private boolean once = true;
private final float[] matrixValues = new float[9];
private ScaleGestureDetector mScaleGestureDetector = null;
private final Matrix mScaleMatrix = new Matrix();
private GestureDetector mGestureDetector;
private boolean isAutoScale;
private int mTouchSlop;
private float mLastX;
private float mLastY;
private boolean isCanDrag;
private int lastPointerCount;
public ClipZoomImageView(Context context)
{
this(context, null);
}
public ClipZoomImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
setScaleType(ScaleType.MATRIX);
mGestureDetector = new GestureDetector(context,
new GestureDetector.SimpleOnGestureListener()
{
@Override
public boolean onDoubleTap(MotionEvent e)
{
if (isAutoScale == true)
return true;
float x = e.getX();
float y = e.getY();
if (getScale() < SCALE_MID)
{
ClipZoomImageView.this.postDelayed(
new AutoScaleRunnable(SCALE_MID, x, y), 16);
isAutoScale = true;
} else
{
ClipZoomImageView.this.postDelayed(
new AutoScaleRunnable(initScale, x, y), 16);
isAutoScale = true;
}
return true;
}
});
mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(this);
}
private class AutoScaleRunnable implements Runnable
{
static final float BIGGER = 1.07f;
static final float SMALLER = 0.93f;
private float mTargetScale;
private float tmpScale;
private float x;
private float y;
public AutoScaleRunnable(float targetScale, float x, float y)
{
this.mTargetScale = targetScale;
this.x = x;
this.y = y;
if (getScale() < mTargetScale)
{
tmpScale = BIGGER;
} else
{
tmpScale = SMALLER;
}
}
@Override
public void run()
{
mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
checkBorder();
setImageMatrix(mScaleMatrix);
final float currentScale = getScale();
if (((tmpScale > 1f) && (currentScale < mTargetScale))
|| ((tmpScale < 1f) && (mTargetScale < currentScale)))
{
ClipZoomImageView.this.postDelayed(this, 16);
} else
{
final float deltaScale = mTargetScale / currentScale;
mScaleMatrix.postScale(deltaScale, deltaScale, x, y);
checkBorder();
setImageMatrix(mScaleMatrix);
isAutoScale = false;
}
}
}
@Override
public boolean onScale(ScaleGestureDetector detector)
{
float scale = getScale();
float scaleFactor = detector.getScaleFactor();
if (getDrawable() == null)
return true;
if ((scale < SCALE_MAX && scaleFactor > 1.0f)
|| (scale > initScale && scaleFactor < 1.0f))
{
if (scaleFactor * scale < initScale)
{
scaleFactor = initScale / scale;
}
if (scaleFactor * scale > SCALE_MAX)
{
scaleFactor = SCALE_MAX / scale;
}
mScaleMatrix.postScale(scaleFactor, scaleFactor,
detector.getFocusX(), detector.getFocusY());
checkBorder();
setImageMatrix(mScaleMatrix);
}
return true;
}
private RectF getMatrixRectF()
{
Matrix matrix = mScaleMatrix;
RectF rect = new RectF();
Drawable d = getDrawable();
if (null != d)
{
rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
matrix.mapRect(rect);
}
return rect;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector)
{
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector)
{
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
if (mGestureDetector.onTouchEvent(event))
return true;
mScaleGestureDetector.onTouchEvent(event);
float x = 0, y = 0;
final int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++)
{
x += event.getX(i);
y += event.getY(i);
}
x = x / pointerCount;
y = y / pointerCount;
if (pointerCount != lastPointerCount)
{
isCanDrag = false;
mLastX = x;
mLastY = y;
}
lastPointerCount = pointerCount;
switch (event.getAction())
{
case MotionEvent.ACTION_MOVE:
float dx = x - mLastX;
float dy = y - mLastY;
if (!isCanDrag)
{
isCanDrag = isCanDrag(dx, dy);
}
if (isCanDrag)
{
if (getDrawable() != null)
{
RectF rectF = getMatrixRectF();
if (rectF.width() <= getWidth() - mHorizontalPadding * 2)
{
dx = 0;
}
if (rectF.height() <= getHeight() - mVerticalPadding * 2)
{
dy = 0;
}
mScaleMatrix.postTranslate(dx, dy);
checkBorder();
setImageMatrix(mScaleMatrix);
}
}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
lastPointerCount = 0;
break;
}
return true;
}
public final float getScale()
{
mScaleMatrix.getValues(matrixValues);
return matrixValues[Matrix.MSCALE_X];
}
@Override
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@SuppressWarnings("deprecation")
@Override
protected void onDetachedFromWindow()
{
super.onDetachedFromWindow();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
private int mHorizontalPadding;
private int mVerticalPadding;
@Override
public void onGlobalLayout()
{
if (once)
{
Drawable d = getDrawable();
if (d == null)
return;
mVerticalPadding = (getHeight() - (getWidth() - 2 * mHorizontalPadding)) / 2;
int width = getWidth();
int height = getHeight();
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
float scale = 1.0f;
if (dw < getWidth() - mHorizontalPadding * 2
&& dh > getHeight() - mVerticalPadding * 2)
{
scale = (getWidth() * 1.0f - mHorizontalPadding * 2) / dw;
}
if (dh < getHeight() - mVerticalPadding * 2
&& dw > getWidth() - mHorizontalPadding * 2)
{
scale = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;
}
if (dw < getWidth() - mHorizontalPadding * 2
&& dh < getHeight() - mVerticalPadding * 2)
{
float scaleW = (getWidth() * 1.0f - mHorizontalPadding * 2)
/ dw;
float scaleH = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;
scale = Math.max(scaleW, scaleH);
}
if (dw >= getWidth() - mHorizontalPadding * 2
&& dh >= getHeight() - mVerticalPadding * 2)
{
float scaleW = (getWidth() * 1.0f - mHorizontalPadding * 2) / dw;
float scaleH = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;
scale = Math.max(scaleW, scaleH);
}
initScale = scale;
SCALE_MID = initScale * 2;
SCALE_MAX = initScale * 4;
mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
mScaleMatrix.postScale(scale, scale, getWidth() / 2,
getHeight() / 2);
setImageMatrix(mScaleMatrix);
once = false;
}
}
public Bitmap clip()
{
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
draw(canvas);
return Bitmap.createBitmap(bitmap, mHorizontalPadding,
mVerticalPadding, getWidth() - 2 * mHorizontalPadding,
getWidth() - 2 * mHorizontalPadding);
}
private void checkBorder()
{
RectF rect = getMatrixRectF();
float deltaX = 0;
float deltaY = 0;
int width = getWidth();
int height = getHeight();
if (rect.width() + 0.01 >= width - 2 * mHorizontalPadding)
{
if (rect.left > mHorizontalPadding)
{
deltaX = -rect.left + mHorizontalPadding;
}
if (rect.right < width - mHorizontalPadding)
{
deltaX = width - mHorizontalPadding - rect.right;
}
}
if (rect.height() + 0.01 >= height - 2 * mVerticalPadding)
{
if (rect.top > mVerticalPadding)
{
deltaY = -rect.top + mVerticalPadding;
}
if (rect.bottom < height - mVerticalPadding)
{
deltaY = height - mVerticalPadding - rect.bottom;
}
}
mScaleMatrix.postTranslate(deltaX, deltaY);
}
private boolean isCanDrag(float dx, float dy)
{
return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
}
public void setHorizontalPadding(int mHorizontalPadding)
{
this.mHorizontalPadding = mHorizontalPadding;
}
}
合二为一,裁剪功能的Layout
public class ClipImageLayout extends RelativeLayout
{
private ClipZoomImageView mZoomImageView;
private ClipImageBorderView mClipImageView;
private int mHorizontalPadding = 20;
public ClipImageLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
mZoomImageView = new ClipZoomImageView(context);
mClipImageView = new ClipImageBorderView(context);
}
public void setImageBitmap(Bitmap bitmap){
mZoomImageView.setImageBitmap(bitmap);
android.view.ViewGroup.LayoutParams lp = new RelativeLayout.LayoutParams(
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
this.addView(mZoomImageView, lp);
this.addView(mClipImageView, lp);
mHorizontalPadding = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, mHorizontalPadding, getResources()
.getDisplayMetrics());
mZoomImageView.setHorizontalPadding(mHorizontalPadding);
mClipImageView.setHorizontalPadding(mHorizontalPadding);
}
public void setHorizontalPadding(int mHorizontalPadding)
{
this.mHorizontalPadding = mHorizontalPadding;
}
public Bitmap clip()
{
return mZoomImageView.clip();
}
}
使用
布局文件中写入自定义的ClipImageLayout控件
<com.example.cropimage.ClipImageLayout
android:id="@+id/cl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="250dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
设置图片,完成裁剪
效果