2017-05-23 12 views
1

사용자가 직사각형, 원형 ​​등과 같은 도형을 그릴 수있는 그리기 응용 프로그램을 개발 중입니다. 사용자는 자유로운 손 그리기 (펜)도 만들 수 있습니다.캔버스에서 실행 취소 및 다시 실행

실행 취소, 다시 실행 기능을 추가하고 싶습니다. 검색 및 다시 실행 취소 및 다시 실행에 대한 대부분의 대답을 읽었지만 모든 경로와 관련이 있습니다. 두 개의 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

그리고 무엇 당신을 지적하는

//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(); } 

희망 :

은 확실히 당신이 뭔가를 재실행을 수행 할 수 있도록 다른 목록에서 실행 취소 작업을 저장해야 문제 야? 도형을 취소 하시겠습니까? – CoolMind

답변

2

모든 모양은 경로에 의해 표현 될 수있다.

당신은 아마 같은 모양의 Path 개체 drawCircle, drawArc, drawLine으로 그려진 모든 형태를 변환해야합니다. 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(); 

지금 목록 만

//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); 
    } 
} 

의 모든 경로를 그립니다 된 onDraw 메소드를 구현 목록에이 데이터를 저장 마지막 요소를 목록에서 호출하고 onDraw() 뷰를 요청하기 위해 invalidate()을 호출하면 마지막 경로가 다시 그려지지 않습니다. 올바른 방향 :

+0

큰 도움이됩니다. 그것이 작동 할 수 있기를 바랍니다 –

+0

경로를 사용하여 사각형, 원형을 그리는 방법을 공유 할 수 있습니까? –

+0

난 대답을 업데이 트했습니다 원을 만드는 데 사용할 수있는 두 가지 방법을 추가 경로 및 사각형 경로 – MatPag