使用:xml或创建就行。
缩放功能未做,思路可如下:
定义个 CallBack,缩放后重置裁剪框的大小,定义两个变量保存裁剪框绝对中心点,以及绝对宽高。四个值按照变化除以缩放比例即可, CallBack 回调完成图片对应部分的缩放。
//隐藏 ActionBar
context.getSupportActionBar().hide();
裁剪框:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.Stack;
public class TailoringView extends View {
public TailoringView(Context context) {
super(context, null);
init(context);
}
public TailoringView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TailoringView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public TailoringView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
direction = context.getResources().getConfiguration().getLayoutDirection();
displayWidth = context.getResources().getDisplayMetrics().widthPixels;
displayHeight = context.getResources().getDisplayMetrics().heightPixels;
x = displayWidth >> 1;
y = displayHeight >> 1;
width = displayWidth >> 1;
height = displayHeight >> 1;
mRectPaint.setColor(context.getColor(R.color.black));
mRectPaint.setAlpha(0);
greyPaint.setColor(context.getColor(R.color.black));
greyPaint.setAlpha(100);
mRectPaint.setStrokeWidth(1);
mFramePaint.setColor(context.getColor(R.color.white));
}
/**
* 横竖屏方向
*/
private int direction;
/**
* 裁剪框中心坐标
*/
private float x, y;
/**
* 屏幕宽高,默认获取的屏幕宽高
*/
private int displayWidth, displayHeight;
/**
* 裁剪框宽高
*/
private float width, height;
/**
* 上一次的中心值,宽高值
*/
private final Stack<Float> lastXStack = new Stack<>();
private final Stack<Float> lastYStack = new Stack<>();
private final Stack<Float> lastWStack = new Stack<>();
private final Stack<Float> lastHStack = new Stack<>();
/**
* 点击的X坐标
*/
private float clickX;
/**
* 点击的Y坐标
*/
private float clickY;
/**
* 裁剪框最小宽度,默认200
*/
private float minWidth = 200;
/**
* 裁剪框最小高度,默认200
*/
private float minHeight = 200;
/**
* 最大的边界距离,默认100
*/
private float padding = 100;
/**
* 获取的高度会有问题,该值可以调整
*/
private float heightOffset = 210;
/**
* 0,1,2,3:0左,1上,2右,3下
*/
private int all = -1;
private final Paint mRectPaint = new Paint();
private final Paint mFramePaint = new Paint();
private final Paint greyPaint = new Paint();
@SuppressLint("ResourceAsColor")
@Override
protected void onDraw(Canvas canvas) {
synchronized (this) {
final float diff = Math.max(width / 160, height / 160);
mFramePaint.setStrokeWidth(diff * 2);
float x1 = x - (width / 2);
float x2 = x + (width / 2);
float y1 = y - (height / 2);
float y2 = y + (height / 2);
float diffFrame = Math.min(height / 5, width / 5);
//绘制矩形框
canvas.drawRect(x1, y1, x2, y2, mRectPaint);
//周围置灰
canvas.drawRect(0, 0, x1 + diff, displayHeight, greyPaint);
canvas.drawRect(x1 + diff, 0, displayWidth, y1 + diff, greyPaint);
canvas.drawRect(x2 - diff, y1 + diff, displayWidth, y2 - diff, greyPaint);
canvas.drawRect(x1 + diff, y2 - diff, displayWidth, displayHeight, greyPaint);
//左上角
canvas.drawLine(x1 - diff, y1, x1 + diffFrame, y1, mFramePaint);
canvas.drawLine(x1, y1 - diff, x1, y1 + diffFrame, mFramePaint);
//左下角
canvas.drawLine(x1, y2 + diff, x1, y2 - diffFrame, mFramePaint);
canvas.drawLine(x1 - diff, y2, x1 + diffFrame, y2, mFramePaint);
//右上角
canvas.drawLine(x2 + diff, y1, x2 - diffFrame, y1, mFramePaint);
canvas.drawLine(x2, y1 - diff, x2, y1 + diffFrame, mFramePaint);
//右下角
canvas.drawLine(x2 + diff, y2, x2 - diffFrame, y2, mFramePaint);
canvas.drawLine(x2, y2 + diff, x2, y2 - diffFrame, mFramePaint);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
float diffAll = Math.min(height / 5, width / 5);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if(all != -1) {
//防止速度过快,未产生事件
lastXStack.add(x);
lastYStack.add(y);
lastWStack.add(width);
lastHStack.add(height);
}
final float offX = event.getRawX();
final float offY = event.getRawY();
final float dXValue = offX - clickX;
final float dYValue = offY - clickY;
clickX = offX;
clickY = offY;
//X超边界不可移动
if (x - (width / 2) <= padding && (all == 0 && dXValue < 0)) {
return true;
}
if (x + (width / 2) >= displayWidth - padding && (all == 2 && dXValue > 0)) {
return true;
}
//Y超边界不可移动
if (y - (height / 2) < padding && (all == 1 && dYValue < 0)) {
return true;
}
if (y + (height / 2) >= displayHeight - padding - heightOffset && (all == 3 && dYValue > 0)) {
return true;
}
//宽是否为最小值
if (width <= minWidth) {
if (all == 0 && dXValue > 0) {
return true;
}
if (all == 2 && dXValue < 0) {
return true;
}
}
//高是否为最小值
if (height <= minHeight) {
if (all == 1 && dYValue > 0) {
return true;
}
if (all == 3 && dYValue < 0) {
return true;
}
}
//根据点击点进行值变化
switch (all) {
case 0:
x += (dXValue / 2);
width -= dXValue;
break;
case 1:
y += (dYValue / 2);
height -= dYValue;
break;
case 2:
x += (dXValue / 2);
width += dXValue;
break;
case 3:
y += (dYValue / 2);
height += dYValue;
break;
default:
break;
}
if (all != -1) {
invalidate();
}
break;
case MotionEvent.ACTION_DOWN:
float x1 = x - (width / 2);
float x2 = x + (width / 2);
float y1 = y - (height / 2);
float y2 = y + (height / 2);
//需要移动的边
clickX = event.getRawX();
clickY = event.getRawY() - heightOffset;
if (Math.abs(clickX - x1) <= diffAll && clickY >= y1 && clickY <= y2) {
//点击左边界
all = 0;
} else if (Math.abs(clickY - y1) <= diffAll && clickX >= x1 && clickX <= x2) {
//点击上边界
all = 1;
} else if (Math.abs(clickX - x2) <= diffAll && clickY >= y1 && clickY <= y2) {
//点击右边界
all = 2;
} else if (Math.abs(clickY - y2) <= diffAll && clickX >= x1 && clickX <= x2) {
//点击下边界
all = 3;
} else {
all = -1;
}
if(all != -1) {
lastXStack.add(x);
lastYStack.add(y);
lastWStack.add(width);
lastHStack.add(height);
}
break;
case MotionEvent.ACTION_UP:
if(x - (width / 2) <= padding) {
popValue();
while (x - (width / 2) <= padding) {
popValue();
}
}
if(x + (width / 2) >= displayWidth - padding) {
popValue();
while (x + (width / 2) >= displayWidth - padding) {
popValue();
}
}
if(y - (height / 2) < padding) {
popValue();
while (y - (height / 2) < padding ) {
popValue();
}
}
if(y + (height / 2) >= displayHeight - padding - heightOffset) {
popValue();
while (y + (height / 2) >= displayHeight - padding - heightOffset) {
popValue();
}
}
if(width <= minWidth) {
popValue();
while (width <= minWidth) {
popValue();
}
}
if(height <= minHeight) {
popValue();
while (height <= minHeight) {
popValue();
}
}
invalidate();
break;
default:
break;
}
return true;
}
private void reset() {
lastXStack.clear();
lastYStack.clear();
lastWStack.clear();
lastHStack.clear();
x = displayWidth >> 1;
y = displayHeight >> 1;
width = displayWidth >> 1;
height = displayHeight >> 1;
lastXStack.add(x);
lastYStack.add(y);
lastWStack.add(width);
lastHStack.add(height);
}
/**
* MOVE 事件产生慢,导致UI异常,进行值回溯防止异常
*/
private void popValue() {
if(lastHStack.size() == 0 || lastWStack.size() == 0 || lastXStack.size() == 0 || lastYStack.size() == 0) {
reset();
return;
}
x = lastXStack.pop();
y = lastYStack.pop();
height = lastHStack.pop();
width = lastWStack.pop();
invalidate();
}
/**
* 获取X值
* @return 裁剪框中心点的 x 坐标
*/
public float getMX() {
return x;
}
/**
* 获取Y值
* @return 裁剪框中心点的 Y 坐标
*/
public float getMY() {
return y;
}
/**
* 获取裁剪框宽
* @return 裁剪框的宽值
*/
public float getMWidth() {
return width;
}
/**
* 获取裁剪框高
* @return 裁剪框的高值
*/
public float getMHeight() {
return height;
}
/**
* 设置 View 视图最大宽度
* @param displayWidth 最大宽度
*/
public void setDisplayWidth(int displayWidth) {
this.displayWidth = displayWidth;
reset();
}
/**
* 设置 View 视图最大高度
* @param displayHeight 最大高度
*/
public void setDisplayHeight(int displayHeight) {
this.displayHeight = displayHeight;
reset();
}
/**
* 设置裁剪框最小宽度
* @param minWidth 裁剪框最小宽度
*/
public void setMinWidth(float minWidth) {
this.minWidth = minWidth;
reset();
}
/**
* 设置裁剪框最小高度
* @param minHeight 裁剪框最小高度
*/
public void setMinHeight(float minHeight) {
this.minHeight = minHeight;
reset();
}
/**
* 设置裁剪框距离边界线最小差值,超过该值不可再往外移动
* @param padding 最小差距值
*/
public void setPadding(float padding) {
this.padding = padding;
reset();
}
/**
* 设置高度差,获取的高度与真实高度不一致,用该值进行高度调整
* @param heightOffset 高度差
*/
public void setHeightOffset(float heightOffset) {
this.heightOffset = heightOffset;
reset();
}
}
快速复制粘贴:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.Stack;
public class TailoringView extends View {
public TailoringView(Context context) {
super(context, null);
init(context);
}
public TailoringView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TailoringView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public TailoringView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
direction = context.getResources().getConfiguration().getLayoutDirection();
displayWidth = context.getResources().getDisplayMetrics().widthPixels;
displayHeight = context.getResources().getDisplayMetrics().heightPixels;
x = displayWidth >> 1;
y = displayHeight >> 1;
width = displayWidth >> 1;
height = displayHeight >> 1;
mRectPaint.setColor(context.getColor(R.color.black));
mRectPaint.setAlpha(0);
greyPaint.setColor(context.getColor(R.color.black));
greyPaint.setAlpha(100);
mRectPaint.setStrokeWidth(1);
mFramePaint.setColor(context.getColor(R.color.white));
}
/**
* 横竖屏方向
*/
private int direction;
/**
* 裁剪框中心坐标
*/
private float x, y;
/**
* 屏幕宽高,默认获取的屏幕宽高
*/
private int displayWidth, displayHeight;
/**
* 裁剪框宽高
*/
private float width, height;
/**
* 上一次的中心值,宽高值
*/
private final Stack<Float> lastXStack = new Stack<>();
private final Stack<Float> lastYStack = new Stack<>();
private final Stack<Float> lastWStack = new Stack<>();
private final Stack<Float> lastHStack = new Stack<>();
/**
* 点击的X坐标
*/
private float clickX;
/**
* 点击的Y坐标
*/
private float clickY;
/**
* 裁剪框最小宽度,默认200
*/
private float minWidth = 200;
/**
* 裁剪框最小高度,默认200
*/
private float minHeight = 200;
/**
* 最大的边界距离,默认100
*/
private float padding = 100;
/**
* 获取的高度会有问题,该值可以调整
*/
private float heightOffset = 210;
/**
* 0,1,2,3:0左,1上,2右,3下
*/
private int all = -1;
private final Paint mRectPaint = new Paint();
private final Paint mFramePaint = new Paint();
private final Paint greyPaint = new Paint();
@SuppressLint("ResourceAsColor")
@Override
protected void onDraw(Canvas canvas) {
synchronized (this) {
final float diff = Math.max(width / 160, height / 160);
mFramePaint.setStrokeWidth(diff * 2);
float x1 = x - (width / 2);
float x2 = x + (width / 2);
float y1 = y - (height / 2);
float y2 = y + (height / 2);
float diffFrame = Math.min(height / 5, width / 5);
//绘制矩形框
canvas.drawRect(x1, y1, x2, y2, mRectPaint);
//周围置灰
canvas.drawRect(0, 0, x1 + diff, displayHeight, greyPaint);
canvas.drawRect(x1 + diff, 0, displayWidth, y1 + diff, greyPaint);
canvas.drawRect(x2 - diff, y1 + diff, displayWidth, y2 - diff, greyPaint);
canvas.drawRect(x1 + diff, y2 - diff, displayWidth, displayHeight, greyPaint);
//左上角
canvas.drawLine(x1 - diff, y1, x1 + diffFrame, y1, mFramePaint);
canvas.drawLine(x1, y1 - diff, x1, y1 + diffFrame, mFramePaint);
//左下角
canvas.drawLine(x1, y2 + diff, x1, y2 - diffFrame, mFramePaint);
canvas.drawLine(x1 - diff, y2, x1 + diffFrame, y2, mFramePaint);
//右上角
canvas.drawLine(x2 + diff, y1, x2 - diffFrame, y1, mFramePaint);
canvas.drawLine(x2, y1 - diff, x2, y1 + diffFrame, mFramePaint);
//右下角
canvas.drawLine(x2 + diff, y2, x2 - diffFrame, y2, mFramePaint);
canvas.drawLine(x2, y2 + diff, x2, y2 - diffFrame, mFramePaint);
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
float diffAll = Math.min(height / 5, width / 5);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if(all != -1) {
//防止速度过快,未产生事件
lastXStack.add(x);
lastYStack.add(y);
lastWStack.add(width);
lastHStack.add(height);
}
final float offX = event.getRawX();
final float offY = event.getRawY();
final float dXValue = offX - clickX;
final float dYValue = offY - clickY;
clickX = offX;
clickY = offY;
//X超边界不可移动
if (x - (width / 2) <= padding && (all == 0 && dXValue < 0)) {
return true;
}
if (x + (width / 2) >= displayWidth - padding && (all == 2 && dXValue > 0)) {
return true;
}
//Y超边界不可移动
if (y - (height / 2) < padding && (all == 1 && dYValue < 0)) {
return true;
}
if (y + (height / 2) >= displayHeight - padding - heightOffset && (all == 3 && dYValue > 0)) {
return true;
}
//宽是否为最小值
if (width <= minWidth) {
if (all == 0 && dXValue > 0) {
return true;
}
if (all == 2 && dXValue < 0) {
return true;
}
}
//高是否为最小值
if (height <= minHeight) {
if (all == 1 && dYValue > 0) {
return true;
}
if (all == 3 && dYValue < 0) {
return true;
}
}
//根据点击点进行值变化
switch (all) {
case 0:
x += (dXValue / 2);
width -= dXValue;
break;
case 1:
y += (dYValue / 2);
height -= dYValue;
break;
case 2:
x += (dXValue / 2);
width += dXValue;
break;
case 3:
y += (dYValue / 2);
height += dYValue;
break;
default:
break;
}
if (all != -1) {
invalidate();
}
break;
case MotionEvent.ACTION_DOWN:
float x1 = x - (width / 2);
float x2 = x + (width / 2);
float y1 = y - (height / 2);
float y2 = y + (height / 2);
//需要移动的边
clickX = event.getRawX();
clickY = event.getRawY() - heightOffset;
if (Math.abs(clickX - x1) <= diffAll && clickY >= y1 && clickY <= y2) {
//点击左边界
all = 0;
} else if (Math.abs(clickY - y1) <= diffAll && clickX >= x1 && clickX <= x2) {
//点击上边界
all = 1;
} else if (Math.abs(clickX - x2) <= diffAll && clickY >= y1 && clickY <= y2) {
//点击右边界
all = 2;
} else if (Math.abs(clickY - y2) <= diffAll && clickX >= x1 && clickX <= x2) {
//点击下边界
all = 3;
} else {
all = -1;
}
if(all != -1) {
lastXStack.add(x);
lastYStack.add(y);
lastWStack.add(width);
lastHStack.add(height);
}
break;
case MotionEvent.ACTION_UP:
if(x - (width / 2) <= padding) {
popValue();
while (x - (width / 2) <= padding) {
popValue();
}
}
if(x + (width / 2) >= displayWidth - padding) {
popValue();
while (x + (width / 2) >= displayWidth - padding) {
popValue();
}
}
if(y - (height / 2) < padding) {
popValue();
while (y - (height / 2) < padding ) {
popValue();
}
}
if(y + (height / 2) >= displayHeight - padding - heightOffset) {
popValue();
while (y + (height / 2) >= displayHeight - padding - heightOffset) {
popValue();
}
}
if(width <= minWidth) {
popValue();
while (width <= minWidth) {
popValue();
}
}
if(height <= minHeight) {
popValue();
while (height <= minHeight) {
popValue();
}
}
invalidate();
break;
default:
break;
}
return true;
}
private void reset() {
lastXStack.clear();
lastYStack.clear();
lastWStack.clear();
lastHStack.clear();
x = displayWidth >> 1;
y = displayHeight >> 1;
width = displayWidth >> 1;
height = displayHeight >> 1;
lastXStack.add(x);
lastYStack.add(y);
lastWStack.add(width);
lastHStack.add(height);
}
/**
* MOVE 事件产生慢,导致UI异常,进行值回溯防止异常
*/
private void popValue() {
if(lastHStack.size() == 0 || lastWStack.size() == 0 || lastXStack.size() == 0 || lastYStack.size() == 0) {
reset();
return;
}
x = lastXStack.pop();
y = lastYStack.pop();
height = lastHStack.pop();
width = lastWStack.pop();
invalidate();
}
/**
* 获取X值
* @return 裁剪框中心点的 x 坐标
*/
public float getMX() {
return x;
}
/**
* 获取Y值
* @return 裁剪框中心点的 Y 坐标
*/
public float getMY() {
return y;
}
/**
* 获取裁剪框宽
* @return 裁剪框的宽值
*/
public float getMWidth() {
return width;
}
/**
* 获取裁剪框高
* @return 裁剪框的高值
*/
public float getMHeight() {
return height;
}
/**
* 设置 View 视图最大宽度
* @param displayWidth 最大宽度
*/
public void setDisplayWidth(int displayWidth) {
this.displayWidth = displayWidth;
reset();
}
/**
* 设置 View 视图最大高度
* @param displayHeight 最大高度
*/
public void setDisplayHeight(int displayHeight) {
this.displayHeight = displayHeight;
reset();
}
/**
* 设置裁剪框最小宽度
* @param minWidth 裁剪框最小宽度
*/
public void setMinWidth(float minWidth) {
this.minWidth = minWidth;
reset();
}
/**
* 设置裁剪框最小高度
* @param minHeight 裁剪框最小高度
*/
public void setMinHeight(float minHeight) {
this.minHeight = minHeight;
reset();
}
/**
* 设置裁剪框距离边界线最小差值,超过该值不可再往外移动
* @param padding 最小差距值
*/
public void setPadding(float padding) {
this.padding = padding;
reset();
}
/**
* 设置高度差,获取的高度与真实高度不一致,用该值进行高度调整
* @param heightOffset 高度差
*/
public void setHeightOffset(float heightOffset) {
this.heightOffset = heightOffset;
reset();
}
}