【问题标题】:Undo method for a paint program [duplicate]绘画程序的撤消方法[重复]
【发布时间】:2014-06-29 18:20:54
【问题描述】:

我正在尝试在我的一个程序中创建一个小绘图板。这是课程:

class DrawPad extends JComponent {

Image image;
Graphics2D graphics;
int currentX, currentY, oldX, oldY;

public DrawPad() {
    setDoubleBuffered(false);        
    addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            oldX = e.getX();
            oldY = e.getY();
        }
    });

    addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            currentX = e.getX();
            currentY = e.getY();
            if (graphics != null) {
                graphics.drawLine(oldX, oldY, currentX, currentY);
            }
            repaint();
            oldX = currentX;
            oldY = currentY;
        }           
    });
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (image == null) {
        image = createImage(getSize().width, getSize().height);
        graphics = (Graphics2D) image.getGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        clear();
    }
    g.drawImage(image, 0, 0, null);
}

public void clear() {
    graphics.setPaint(Color.white);
    graphics.fillRect(0, 0, getSize().width, getSize().height);
    graphics.setPaint(Color.black);
    repaint();
}

public void undo() {
    // restore previous graphics here
}

public void red() {
    graphics.setPaint(Color.red);
    repaint();
}

public void black() {
    graphics.setPaint(Color.black);
    repaint();
}

public void magenta() {
    graphics.setPaint(Color.magenta);
    repaint();
}

public void blue() {
    graphics.setPaint(Color.blue);
    repaint();
}

public void green() {
    graphics.setPaint(Color.green);
    repaint();
}

}

我将它添加到 JPanel。绘图本身工作正常。现在我想实现一个撤消方法。我以为我可以将当​​前的 graphics2D 对象复制到备份变量中,然后单击撤消按钮,我将用备份对象替换当前对象。 不幸的是,这不起作用。

我尝试了以下方法:

在 mousePressed 方法中,我将图形的当前值赋给了备份变量。

addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
        graphicsBackup = graphics.create();
        oldX = e.getX();
        oldY = e.getY();
    }
});

在撤消方法中,我尝试将备份变量的引用分配给原始对象。

public void undo() {
    graphics = graphicsBackup;
    repaint();
}

在第二次尝试中,我使用了 AffineTransform 对象。我在 keyPressed 方法中调用 getTransform 来获取当前状态,然后在 undo 方法中调用 setTransform。 那也没有用。您对它的工作方式有什么建议吗?


解决方案:(添加了一些额外的背景方法等)

class DrawPad extends JComponent {

private Image image;
private Image background;
private Graphics2D graphics;
private int currentX, currentY, oldX, oldY;
private final SizedStack<Image> undoStack = new SizedStack<>(12);

public DrawPad() {
    setDoubleBuffered(false);
    addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
            saveToStack(image);
            oldX = e.getX();
            oldY = e.getY();
        }
    });

    addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            currentX = e.getX();
            currentY = e.getY();
            if (graphics != null) {
                graphics.drawLine(oldX, oldY, currentX, currentY);
            }
            repaint();
            oldX = currentX;
            oldY = currentY;
        }
    });
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (image == null) {
        image = createImage(getSize().width, getSize().height);
        graphics = (Graphics2D) image.getGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        clear();
    }
    g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}

public void clear() {
    if (background != null) {
        setImage(copyImage(background));
    } else {
        graphics.setPaint(Color.white);
        graphics.fillRect(0, 0, getSize().width, getSize().height);
        graphics.setPaint(Color.black);
    }
    repaint();
}

public void undo() {
    if (undoStack.size() > 0) {
        setImage(undoStack.pop());
    }
}

private void setImage(Image img) {
    graphics = (Graphics2D) img.getGraphics();
    graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    graphics.setPaint(Color.black);
    image = img;
    repaint();
}

public void setBackground(Image img) {
    background = copyImage(img);
    setImage(copyImage(img));
}

private BufferedImage copyImage(Image img) {
    BufferedImage copyOfImage = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_RGB);
    Graphics g = copyOfImage.createGraphics();
    g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
    return copyOfImage;
}

private void saveToStack(Image img) {
    undoStack.push(copyImage(img));
}

public void red() {
    graphics.setPaint(Color.red);
    repaint();
}

public void black() {
    graphics.setPaint(Color.black);
    repaint();
}

public void magenta() {
    graphics.setPaint(Color.magenta);
    repaint();
}

public void blue() {
    graphics.setPaint(Color.blue);
    repaint();
}

public void green() {
    graphics.setPaint(Color.green);
    repaint();
}
}

大小堆栈:

public class SizedStack<T> extends Stack<T> {

private final int maxSize;

public SizedStack(int size) {
    super();
    this.maxSize = size;
}

@Override
public Object push(Object object) {
    while (this.size() > maxSize) {
        this.remove(0);
    }
    return super.push((T) object);
}
}

【问题讨论】:

  • 我无法将 UndoManager 添加到 Graphics2D 对象、图像或 JComponent。我必须在哪里添加它?

标签: java graphics awt


【解决方案1】:

不久前我自己实现了一个绘图程序。以下是我实现撤消/重做的方式:

我有两个堆栈。 “撤消”堆栈和“重做”堆栈。每次用户画东西时,我都是这样做的:

  1. 应用绘制到图像的图形之前,我将图像保存到“撤消”堆栈中。
  2. 应用所绘制的图形后,我将图像再次保存到“撤消”堆栈中。

例如,如果用户想画一条线,我将当前图像保存到堆栈中将该线应用于图像之前。应用该行后,我将其再次保存到堆栈中。

当用户按下“撤消”时,我只需从“撤消”堆栈中弹出顶部图像,并将其绘制到屏幕上。然后我把它放在'redo'堆栈中。

当用户按下“重做”时,“重做”堆栈中的顶部图像被弹出并应用于绘图区域,并放在“撤消”堆栈的顶部(反向操作)。

编辑:为了将图像推入堆栈(堆栈中的所有对象都不引用同一对象),您需要先将图像复制到新图像。像这样的东西(可能在细节上不准确):

void saveToStack(BufferedImage img){ // makes a copy of img and puts on stack.
    BufferedImage imageForStack = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
    Graphics2D g2d = imageForStack.createGraphics();
    g2d.drawImage(img, 0, 0, null);
    undoStack.push(imageForStack);
}

希望这会有所帮助。

【讨论】:

  • 确实有帮助。我创建了一个 Stack 但我将如何创建变量“image”的新实例以将其推送到堆栈上。现在堆栈中充满了对同一对象的引用。
  • 谢谢,我明天尽快试试。
  • 完美运行。添加了问题的解决方案。我只实现了“撤消”部分。
  • @Moh-Aw 很高兴我能帮上忙 :)
  • 如果您想限制撤消步骤以不耗尽内存,您可能会使用数组队列并在达到限制时出列或覆盖之前添加的状态。
猜你喜欢
  • 2016-09-20
  • 2011-04-26
  • 2015-03-30
  • 2014-01-16
  • 1970-01-01
  • 2014-04-19
  • 2011-09-11
  • 2011-05-21
  • 1970-01-01
相关资源
最近更新 更多