在 android 中撤消在自定义图像视图上的绘制
Undo drawing on the custom image view in android
我正在开发绘图应用程序,到目前为止我可以在图像上画线,问题是我发现它无法在自定义图像视图中集成撤消操作。
我采用了“一条条存储绘制路径,并在onDraw语句上绘制”的逻辑,但代码中似乎存在一些缺失/缺陷。
如果您想了解有关代码的更多详细信息,欢迎提出任何问题。屏幕截图是我的应用程序的样子(在图像上绘制)
感谢您的帮助。
在主Activity中:
// set image
bitmap = downScale(view.getTag().toString(),1280,1024);
altered_bitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
draw_view.setNewImage(altered_bitmap,bitmap);
undo.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
if (pencil.getVisibility() == View.VISIBLE || pen.getVisibility() == View.VISIBLE) {
draw_view.onClickUndo();
}
}
});
并且在自定义图像视图中:
private ArrayList<Path> paths = new ArrayList<Path>();
private Path mPath;
public ScaleImageView(Context context) {
super(context);
sharedConstructing(context);
}
public void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAlpha(alpha);
drawListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (getDrawable() != null) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];// event.getX();
downy = getPointerCoords(event)[1];// event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
mPath = new Path();
paths.add(mPath);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
mPath = new Path();
paths.add(mPath);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
}
return true;
}
};
setOnTouchListener(drawListener);
}
//draw view start
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp) {
canvas = new Canvas(alteredBitmap);
matrix_draw = new Matrix();
canvas.drawBitmap(bmp, matrix_draw, paint);
setImageBitmap(alteredBitmap);
mPath = new Path();
paths.add(mPath);
}
public void setBrushColor(int color) {
this.color = color;
paint.setColor(color);
paint.setAlpha(alpha);
}
public void setAlpha(int alpha) {
this.alpha = alpha;
paint.setAlpha(alpha);
}
public void setWidth(float width) {
this.width = width;
paint.setStrokeWidth(width);
}
final float[] getPointerCoords(MotionEvent e) {
final int index = e.getActionIndex();
final float[] coords = new float[] { e.getX(index), e.getY(index) };
Matrix matrix = new Matrix();
getImageMatrix().invert(matrix);
matrix.postTranslate(getScrollX(), getScrollY());
matrix.mapPoints(coords);
return coords;
}
public void setIsScale() {
isScale = !isScale;
setOnTouchListener(isScale ? zoomListener : drawListener);
}
@Override
protected void onDraw(Canvas canvas) {
for (Path p : paths){
canvas.drawPath(p, paint_line);
}
}
public void onClickUndo () {
if (paths.size()>0){
paths.remove(paths.size()-1);
invalidate();
}
}
//draw view end
更新:测试结果
经过一段时间的测试,发现应用程序运行但所选图像不是像这样绘制在自定义图像视图中:
如果你有空闲时间,我已经上传了项目(<1 mb),
这是一个小型绘图工具,首先将一个包含一些图像的文件夹复制到您设备
中的文件夹"HistoryTool"
路径,例如:
sd card root/ HistoryTool/ folder1 / a.jpg
,然后你就可以在上面画画了,仅此而已,但是现在撤消操作不起作用,需要修复。
https://drive.google.com/file/d/0B9mELZtUJp0LLVh0b1Q0a3VTcG8/view?usp=sharing
非常感谢
实际上我想我已经找到问题的根源:每次捕获 MotionEvent.ACTION_MOVE 时都会添加一条新路径,但是触摸传感器非常嘈杂并且您会收到很多这样的事件。您可能会考虑编辑您在检测到 MotionEvent.ACTION_DOWN 时添加的现有路径。我会考虑做这样的事情
Path currentPath = null;
drawListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (getDrawable() != null) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];// event.getX();
downy = getPointerCoords(event)[1];// event.getY();
currentPath = new Path();
currentPath.moveTo(downx, downy);
paths.add(currentPath);
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
currentPath.lineTo(upx, upy);
invalidate();
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
currentPath.lineTo(upx, upy);
invalidate();
currentPath = null;
break;
case MotionEvent.ACTION_CANCEL:
currentPath = null;
break;
default:
break;
}
}
return true;
}
};
只需确保将 currentPath 声明为自定义视图的私有字段 class。对不起,我现在不能测试代码,所以如果你对这段代码有什么问题,请告诉我,晚上我会给你一个测试过的代码。
编辑:抱歉,我在 ACTION_MOVE 处理中添加了额外的 moveTo,这将阻止正确的工作代码
我正在开发绘图应用程序,到目前为止我可以在图像上画线,问题是我发现它无法在自定义图像视图中集成撤消操作。
我采用了“一条条存储绘制路径,并在onDraw语句上绘制”的逻辑,但代码中似乎存在一些缺失/缺陷。
如果您想了解有关代码的更多详细信息,欢迎提出任何问题。屏幕截图是我的应用程序的样子(在图像上绘制)
感谢您的帮助。
在主Activity中:
// set image
bitmap = downScale(view.getTag().toString(),1280,1024);
altered_bitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), bitmap.getConfig());
draw_view.setNewImage(altered_bitmap,bitmap);
undo.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
if (pencil.getVisibility() == View.VISIBLE || pen.getVisibility() == View.VISIBLE) {
draw_view.onClickUndo();
}
}
});
并且在自定义图像视图中:
private ArrayList<Path> paths = new ArrayList<Path>();
private Path mPath;
public ScaleImageView(Context context) {
super(context);
sharedConstructing(context);
}
public void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix = new Matrix();
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(width);
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAlpha(alpha);
drawListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (getDrawable() != null) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];// event.getX();
downy = getPointerCoords(event)[1];// event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
mPath = new Path();
paths.add(mPath);
invalidate();
downx = upx;
downy = upy;
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
mPath = new Path();
paths.add(mPath);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
}
return true;
}
};
setOnTouchListener(drawListener);
}
//draw view start
public void setNewImage(Bitmap alteredBitmap, Bitmap bmp) {
canvas = new Canvas(alteredBitmap);
matrix_draw = new Matrix();
canvas.drawBitmap(bmp, matrix_draw, paint);
setImageBitmap(alteredBitmap);
mPath = new Path();
paths.add(mPath);
}
public void setBrushColor(int color) {
this.color = color;
paint.setColor(color);
paint.setAlpha(alpha);
}
public void setAlpha(int alpha) {
this.alpha = alpha;
paint.setAlpha(alpha);
}
public void setWidth(float width) {
this.width = width;
paint.setStrokeWidth(width);
}
final float[] getPointerCoords(MotionEvent e) {
final int index = e.getActionIndex();
final float[] coords = new float[] { e.getX(index), e.getY(index) };
Matrix matrix = new Matrix();
getImageMatrix().invert(matrix);
matrix.postTranslate(getScrollX(), getScrollY());
matrix.mapPoints(coords);
return coords;
}
public void setIsScale() {
isScale = !isScale;
setOnTouchListener(isScale ? zoomListener : drawListener);
}
@Override
protected void onDraw(Canvas canvas) {
for (Path p : paths){
canvas.drawPath(p, paint_line);
}
}
public void onClickUndo () {
if (paths.size()>0){
paths.remove(paths.size()-1);
invalidate();
}
}
//draw view end
更新:测试结果
经过一段时间的测试,发现应用程序运行但所选图像不是像这样绘制在自定义图像视图中:
如果你有空闲时间,我已经上传了项目(<1 mb), 这是一个小型绘图工具,首先将一个包含一些图像的文件夹复制到您设备
中的文件夹"HistoryTool"路径,例如:
sd card root/ HistoryTool/ folder1 / a.jpg
,然后你就可以在上面画画了,仅此而已,但是现在撤消操作不起作用,需要修复。
https://drive.google.com/file/d/0B9mELZtUJp0LLVh0b1Q0a3VTcG8/view?usp=sharing
非常感谢
实际上我想我已经找到问题的根源:每次捕获 MotionEvent.ACTION_MOVE 时都会添加一条新路径,但是触摸传感器非常嘈杂并且您会收到很多这样的事件。您可能会考虑编辑您在检测到 MotionEvent.ACTION_DOWN 时添加的现有路径。我会考虑做这样的事情
Path currentPath = null;
drawListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (getDrawable() != null) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = getPointerCoords(event)[0];// event.getX();
downy = getPointerCoords(event)[1];// event.getY();
currentPath = new Path();
currentPath.moveTo(downx, downy);
paths.add(currentPath);
break;
case MotionEvent.ACTION_MOVE:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
currentPath.lineTo(upx, upy);
invalidate();
break;
case MotionEvent.ACTION_UP:
upx = getPointerCoords(event)[0];// event.getX();
upy = getPointerCoords(event)[1];// event.getY();
currentPath.lineTo(upx, upy);
invalidate();
currentPath = null;
break;
case MotionEvent.ACTION_CANCEL:
currentPath = null;
break;
default:
break;
}
}
return true;
}
};
只需确保将 currentPath 声明为自定义视图的私有字段 class。对不起,我现在不能测试代码,所以如果你对这段代码有什么问题,请告诉我,晚上我会给你一个测试过的代码。
编辑:抱歉,我在 ACTION_MOVE 处理中添加了额外的 moveTo,这将阻止正确的工作代码