2017-05-23 96 views
1

我正在开发绘图应用程序,用户可以在其中绘制矩形,圆等形状。用户还可以进行自由绘制(笔)。画布上的撤销和重做

我想添加撤消,重做功能。我已经搜索并阅读了大部分SO回答以供撤消和重做,但都与路径有关。意味着他们正在管理两个列表,一个用于绘制路径列表,或者第二个用于撤消路径列表。它是一种很好的工作方式,但它的唯一工作是如果它是一张免费的手绘图或我使用路径。

在这里我称不同形状的帆布方法的不同类型。

帮助我在画布上绘制撤消重做。

这里是我的代码

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

什么是一个问题?撤消形状? – CoolMind

回答

2

每个形状可以通过路径来表示。

你可能需要绘制drawCircledrawArcdrawLine同样形状的物体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(); 
} 

希望有你指出了正确的方向:)

+0

伟大的帮助。希望它能工作 –

+0

请分享如何绘制矩形,使用路径的圆圈? –

+0

我已经更新了答案,添加2种方法可以用来创建一个圆形路径和一个矩形路径 – MatPag