我正在开发绘图应用程序,用户可以在其中绘制像矩形,圆形等形状。用户也可以制作自由手绘图(笔)。
我想添加撤消,重做功能。我已经搜索并阅读了大部分撤消和重做的SO答案,但都与路径有关。意味着他们正在管理两个List,一个用于绘制路径列表,另一个用于撤消路径列表。它是一种很好的工作方式,但如果它是一个免费的手绘图或者如果我使用路径它只能工作。
这里我为不同的形状调用不同类型的画布方法。
帮我在画布上绘制撤消重做。
这是我的代码
public class DrawingView extends android.support.v7.widget.AppCompatImageView {
public static final int RECTANGLE = 1;
public static final int SQUARE = 2;
public static final int CIRCLE = 3;
public
static final int LINE = 4;
public static final int SMOOTH_LINE = 5;
public static final int TRIANGLE = 6;
public static final int IMPORT_IMAGE = 7;
public static final int ERASER = 8;
private static final float TOUCH_TOLERANCE = 5;
private int color;
private int currentShape;
protected Paint mPaint;
protected Bitmap mBitmap;
protected Canvas mCanvas;
private float mx, my;
private float mStartX, mStartY;
private int width, height;
private boolean isDrawing = true;
public static int TOUCH_STROKE_WIDTH = 3;
public static int ERASER_WIDTH = 3;
private Path mPath = new Path();
int countTouch = 0;
float basexTriangle = 0;
float baseyTriangle = 0;
public DrawingView(Context context, int shape, int color) {
super(context);
initPaint();
}
protected void initPaint() {
color = DrawingActivity.selectedColor;
currentShape = DrawingActivity.currentShape;
mPaint = new Paint(Paint.DITHER_FLAG);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(color);
if (DrawingActivity.isFill && !DrawingActivity.isEraser && currentShape != SMOOTH_LINE) {
mPaint.setStyle(Paint.Style.FILL);
} else {
mPaint.setStyle(Paint.Style.STROKE);
}
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
if (DrawingActivity.isEraser) {
mPaint.setStrokeWidth(ERASER_WIDTH);
} else {
mPaint.setStrokeWidth(TOUCH_STROKE_WIDTH);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//Retrieve the point
mx = event.getX();
my = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initPaint();
break;
}
switch (currentShape) {
case RECTANGLE:
onTouchEventRectangle(event);
break;
case SQUARE:
onTouchEventSquare(event);
break;
case CIRCLE:
onTouchEventCircle(event);
break;
case LINE:
onTouchEventLine(event);
break;
case SMOOTH_LINE:
onTouchEventSmoothLine(event);
break;
case TRIANGLE:
onTouchEventTriangle(event);
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
if (isDrawing) {
switch (currentShape) {
case RECTANGLE:
onDrawRectangle(canvas);
break;
case SQUARE:
onDrawSquare(canvas);
break;
case CIRCLE:
onDrawCircle(canvas);
break;
case LINE:
onDrawLine(canvas);
break;
case SMOOTH_LINE:
onDrawLine(canvas);
break;
case TRIANGLE:
onDrawTriangle(canvas);
break;
}
}
//draw your element
}
private void onTouchEventRectangle(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
drawRectangle(mCanvas, mPaint);
invalidate();
break;
}
}
private void onDrawRectangle(Canvas canvas) {
drawRectangle(canvas, mPaint);
}
private void drawRectangle(Canvas canvas, Paint paint) {
float right = mStartX > mx ? mStartX : mx;
float left = mStartX > mx ? mx : mStartX;
float bottom = mStartY > my ? mStartY : my;
float top = mStartY > my ? my : mStartY;
canvas.drawRect(left, top, right, bottom, paint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
mCanvas = new Canvas(mBitmap);
}
private void onDrawSquare(Canvas canvas) {
onDrawRectangle(canvas);
}
private void onTouchEventSquare(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
adjustSquare(mx, my);
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
adjustSquare(mx, my);
drawRectangle(mCanvas, mPaint);
invalidate();
break;
}
}
/**
* Adjusts current coordinates to build a square
*
* @param x
* @param y
*/
protected void adjustSquare(float x, float y) {
float deltaX = Math.abs(mStartX - x);
float deltaY = Math.abs(mStartY - y);
float max = Math.max(deltaX, deltaY);
mx = mStartX - x < 0 ? mStartX + max : mStartX - max;
my = mStartY - y < 0 ? mStartY + max : mStartY - max;
}
private void onDrawCircle(Canvas canvas) {
canvas.drawCircle(mStartX, mStartY, calculateRadius(mStartX, mStartY, mx, my), mPaint);
}
private void onTouchEventCircle(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mCanvas.drawCircle(mStartX, mStartY,
calculateRadius(mStartX, mStartY, mx, my), mPaint);
invalidate();
break;
}
}
/**
* @return
*/
protected float calculateRadius(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt(
Math.pow(x1 - x2, 2) +
Math.pow(y1 - y2, 2)
);
}
private void onDrawLine(Canvas canvas) {
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
canvas.drawLine(mStartX, mStartY, mx, my, mPaint);
}
}
private void onTouchEventLine(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mCanvas.drawLine(mStartX, mStartY, mx, my, mPaint);
invalidate();
break;
}
}
private void onTouchEventSmoothLine(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDrawing = true;
mStartX = mx;
mStartY = my;
mPath.reset();
mPath.moveTo(mx, my);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(mx - mStartX);
float dy = Math.abs(my - mStartY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mStartX, mStartY, (mx + mStartX) / 2, (my + mStartY) / 2);
mStartX = mx;
mStartY = my;
}
mCanvas.drawPath(mPath, mPaint);
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrawing = false;
mPath.lineTo(mStartX, mStartY);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
invalidate();
break;
}
}
private void onDrawTriangle(Canvas canvas) {
if (countTouch < 3) {
canvas.drawLine(mStartX, mStartY, mx, my, mPaint);
} else if (countTouch == 3) {
canvas.drawLine(mx, my, mStartX, mStartY, mPaint);
canvas.drawLine(mx, my, basexTriangle, baseyTriangle, mPaint);
}
}
private void onTouchEventTriangle(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
countTouch++;
if (countTouch == 1) {
isDrawing = true;
mStartX = mx;
mStartY = my;
} else if (countTouch == 3) {
isDrawing = true;
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
invalidate();
break;
case MotionEvent.ACTION_UP:
countTouch++;
isDrawing = false;
if (countTouch < 3) {
basexTriangle = mx;
baseyTriangle = my;
mCanvas.drawLine(mStartX, mStartY, mx, my, mPaint);
} else if (countTouch >= 3) {
mCanvas.drawLine(mx, my, mStartX, mStartY, mPaint);
mCanvas.drawLine(mx, my, basexTriangle, baseyTriangle, mPaint);
countTouch = 0;
}
invalidate();
break;
}
}
public void clearDrawing()
{
setDrawingCacheEnabled(false);
onSizeChanged(width, height, width, height);
invalidate();
setDrawingCacheEnabled(true);
}
/**
* Getter of currentShape
*/
public int getCurrentShape() {
return currentShape;
}
/**
* Setter of currentShape
*/
public void setCurrentShape(int currentShape) {
this.currentShape = currentShape;
}
}
答案 0 :(得分:2)
每个形状都可以用路径表示。
您可能需要将使用drawCircle
,drawArc
,drawLine
绘制的每个形状转换为具有相同形状的Path
个对象。
Path类具有创建预定义形状所需的所有方法。
例子:
圈子路径:path.addCircle(float x, float y, float radius, Path.Direction dir)
矩形路径:path.addRect(float left, float top, float right, float bottom, Path.Direction dir)
使用类似这样的类来表示用户的绘制操作:
public class DrawAction {
public Path path;
public Paint paint;
public DrawAction(Path path, Paint paint){
this.path = path;
this.paint = paint;
}
}
然后将此数据保存在列表中
//class property
List<DrawAction> actionsList = new ArrayList<>();
...
//add the path and the paint to a DrawAction object when the user
//want to draw something
actionsList.add(new DrawAction(path, paint));
invalidate();
实现onDraw方法以仅绘制列表中的所有路径
//draw all the paths in your onDraw() method
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (DrawAction actionToDraw : actionsList){
canvas.drawPath(actionToDraw.path, actionToDraw.paint);
}
}
现在,如果您需要重做操作,可以从列表中删除最后一个元素并调用invalidate()
以请求调用视图onDraw()
,这样就不会绘制最后一个路径试。
显然你需要在另一个列表中保存撤消操作才能执行重做,如下所示:
//class property
List<DrawAction> removedPathList = new ArrayList<>();
if (actionsList.size() > 0){
DrawAction undoAction = actionsList.get(actionsList.size() - 1);
removedPathList.add(undoAction);
actionsList.remove(undoAction);
invalidate();
}
希望能指出正确的方向:)