gpt4 book ai didi

android - 在 Canvas 上撤消和重做

转载 作者:行者123 更新时间:2023-11-29 16:57:59 25 4
gpt4 key购买 nike

我正在开发绘图应用程序,用户可以在其中绘制矩形、圆形等形状。用户还可以自由手绘(钢笔)。

我想添加撤消、重做功能。我已经搜索并阅读了大部分关于撤消和重做的 SO 答案,但所有这些都与路径相关。意味着他们正在管理两个列表,一个用于绘制路径列表,另一个用于撤消路径列表。这是一种很好的工作方式,但只有在自由手绘或使用路径时才有效。

这里我为不同的形状调用了不同类型的 Canvas 方法。

帮我在 Canvas 绘图上提供撤消重做。

这是我的代码

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

最佳答案

每个形状都可以用路径表示。

您可能需要将使用 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);
}
}

现在,如果您需要重做一个 Action ,您可以从列表中删除最后一个元素并调用 invalidate() 以请求调用 View 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();
}

希望为您指明了正确的方向:)

关于android - 在 Canvas 上撤消和重做,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44129428/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com