先上效果图
自定义裁剪框view:
public class CropPhotoView extends View {
private static final String TAG = "CropPhotoView";
private float mPreviousTouchX = -1, mPreviousTouchY = -1;
private int mCurrentTouchCornerIndex = -1;
private final RectF mCropViewRect = new RectF();
private final RectF mTempRect = new RectF();
private final RectF mMaxRect = new RectF();
private Paint mCropFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mCropFrameCornersPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
float[] mCropGridCorners;
private int mCropRectCornerTouchAreaLineLength;
private int mCropRectMinSize;
{
mCropRectCornerTouchAreaLineLength = getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_rect_corner_touch_area_line_length);
mCropRectMinSize = getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_rect_min_size);
mCropFramePaint.setColor(getResources().getColor(R.color.color_5164E0));
mCropFramePaint.setStrokeWidth(DensityUtils.dp2px(getContext(), 2));
mCropFramePaint.setStyle(Paint.Style.STROKE);
mCropFrameCornersPaint.setColor(getResources().getColor(R.color.color_5164E0));
mCropFrameCornersPaint.setStrokeWidth(DensityUtils.dp2px(getContext(), 4));
mCropFrameCornersPaint.setStyle(Paint.Style.STROKE);
}
public CropPhotoView(Context context) {
super(context);
}
public CropPhotoView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(mCropViewRect, mCropFramePaint);
canvas.save();
mTempRect.set(mCropViewRect);
mTempRect.inset(mCropRectCornerTouchAreaLineLength, -mCropRectCornerTouchAreaLineLength);
canvas.clipRect(mTempRect, Region.Op.DIFFERENCE);
mTempRect.set(mCropViewRect);
mTempRect.inset(-mCropRectCornerTouchAreaLineLength, mCropRectCornerTouchAreaLineLength);
canvas.clipRect(mTempRect, Region.Op.DIFFERENCE);
canvas.drawRect(mCropViewRect, mCropFrameCornersPaint);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mCropViewRect.isEmpty()) {
return false;
}
float x = event.getX();
float y = event.getY();
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
mCurrentTouchCornerIndex = getCurrentTouchIndex(x, y);
boolean shouldHandle = mCurrentTouchCornerIndex != -1;
if (!shouldHandle) {
mPreviousTouchX = -1;
mPreviousTouchY = -1;
} else if (mPreviousTouchX < 0) {
mPreviousTouchX = x;
mPreviousTouchY = y;
}
return shouldHandle;
}
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE) {
if (event.getPointerCount() == 1 && mCurrentTouchCornerIndex != -1) {
x = Math.min(Math.max(x, getPaddingLeft()), getWidth() - getPaddingRight());
y = Math.min(Math.max(y, getPaddingTop()), getHeight() - getPaddingBottom());
updateCropViewRect(x, y);
mPreviousTouchX = x;
mPreviousTouchY = y;
return true;
}
}
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
mPreviousTouchX = -1;
mPreviousTouchY = -1;
mCurrentTouchCornerIndex = -1;
}
return false;
}
public void setRect(int l, int t, int r, int b) {
mMaxRect.set(l, t, r, b);
mCropViewRect.set(l, t, r, b);
mTempRect.set(l, t, r, b);
updateGridPoints();
postInvalidate();
}
/**
* * The order of the corners is:
* 0------->1
* ^ |
* | 4 |
* | v
* 3<-------2
*/
private void updateCropViewRect(float touchX, float touchY) {
mTempRect.set(mCropViewRect);
Log.e(TAG, "updateCropViewRect: " + mCurrentTouchCornerIndex);
switch (mCurrentTouchCornerIndex) {
// resize rectangle
case 0:
mTempRect.set(Math.max(touchX, mMaxRect.left), Math.max(touchY, mMaxRect.top), mCropViewRect.right, mCropViewRect.bottom);
break;
case 1:
mTempRect.set(mCropViewRect.left, Math.max(touchY, mMaxRect.top), Math.min(touchX, mMaxRect.right), mCropViewRect.bottom);
break;
case 2:
mTempRect.set(mCropViewRect.left, mCropViewRect.top, Math.min(touchX, mMaxRect.right), Math.min(touchY, mMaxRect.bottom));
break;
case 3:
mTempRect.set(Math.max(touchX, mMaxRect.left), mCropViewRect.top, mCropViewRect.right, Math.min(touchY, mMaxRect.bottom));
break;
// move rectangle
case 4:
mTempRect.offset((mTempRect.left + touchX - mPreviousTouchX < mMaxRect.left || mTempRect.right + touchX - mPreviousTouchX > mMaxRect.right) ? 0 : touchX - mPreviousTouchX,
(mTempRect.top + touchY - mPreviousTouchY < mMaxRect.top || mTempRect.bottom + touchY - mPreviousTouchY > mMaxRect.bottom) ? 0 : touchY - mPreviousTouchY);
if (mTempRect.left > getLeft() && mTempRect.top > getTop()
&& mTempRect.right < getRight() && mTempRect.bottom < getBottom()) {
mCropViewRect.set(mTempRect);
updateGridPoints();
postInvalidate();
}
return;
}
boolean changeHeight = mTempRect.height() >= mCropRectMinSize;
boolean changeWidth = mTempRect.width() >= mCropRectMinSize;
mCropViewRect.set(
changeWidth ? mTempRect.left : mCropViewRect.left,
changeHeight ? mTempRect.top : mCropViewRect.top,
changeWidth ? mTempRect.right : mCropViewRect.right,
changeHeight ? mTempRect.bottom : mCropViewRect.bottom);
if (changeHeight || changeWidth) {
updateGridPoints();
postInvalidate();
}
}
private void updateGridPoints() {
mCropGridCorners = RectUtils.getCornersFromRect(mCropViewRect);
}
/**
* * The order of the corners in the float array is:
* 0------->1
* ^ |
* | 4 |
* | v
* 3<-------2
*
* @return - index of corner that is being dragged
*/
private int getCurrentTouchIndex(float touchX, float touchY) {
int closestPointIndex = -1;
double closestPointDistance = getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_rect_corner_touch_threshold);
for (int i = 0; i < 8; i += 2) {
double distanceToCorner = Math.sqrt(Math.pow(touchX - mCropGridCorners[i], 2)
+ Math.pow(touchY - mCropGridCorners[i + 1], 2));
if (distanceToCorner < closestPointDistance) {
closestPointDistance = distanceToCorner;
closestPointIndex = i / 2;
}
}
if (closestPointIndex < 0 && mCropViewRect.contains(touchX, touchY)) {
return 4;
}
return closestPointIndex;
}
public Bitmap getCropBitmap(Bitmap source) {
if (mCropViewRect.contains(mMaxRect)) {
Log.e(TAG, "getCropBitmap: mCropViewRect.contains(mMaxRect)=true");
return source;
}
return Bitmap.createBitmap(source,
(int) ((mCropViewRect.left - mMaxRect.left) / mMaxRect.width() * source.getWidth()),
(int) ((mCropViewRect.top - mMaxRect.top) / mMaxRect.height() * source.getHeight()),
(int) (mCropViewRect.width() / mMaxRect.width() * source.getWidth()),
(int) (mCropViewRect.height() / mMaxRect.height() * source.getHeight()));
}
}
布局文件:
<RelativeLayout
android:id="@+id/rl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toStartOf="@+id/ll_control">
<ImageView
android:id="@+id/iv_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleType="fitXY" />
<com.xxxx.common.widget.CropPhotoView
android:id="@+id/cpv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
ImageView负责显示图片,
CropPhotoView 负责裁剪框。
自定义view借鉴uCrop裁剪框view
由于产品设定比较简单且与uCrop相差甚多,自己借鉴写了一个简单的。