Undo button for drawings in a JFrame

旧时模样 提交于 2021-02-18 19:10:06

问题


I am drawing some objects on a JFrame such as circles, rectangles and arcs between them.

I was wondering how I can implement an Undo button bearing in mind that the ones I found are targeted at text fields and writing in them.

I don't have code to show and I wish to receive advice on how to implement it. Is it possible to 'register' the last event that occurred and then delete it?

I have a very simple code about deleting the circles for instance. It works only for them and I know that it is completely wrong as I could have other events such as rectangles drawing or arcs between them.

Any help is appreciated.


回答1:


You may store the Shape objects in a List, and have your panel draw every shape from this List.

The "Undo" action simply removes the last element and repaints the panel.

The following example has buttons to create rectangles and ellipses at random location, and the famous "Undo" button so that you get the idea.

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToolBar;

public class DrawingUndo {

    List<Shape> shapes = new ArrayList<>();

    public DrawingUndo() {

        final Random randomizer = new Random();

        JFrame frame = new JFrame("Undoable drawings");

        JToolBar bar = new JToolBar();

        final JPanel undoPanel = new UndoPanel();

        JButton addRectangleButton = new JButton("Rectangle");
        addRectangleButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {

                int randomX = 5 + randomizer.nextInt(100);
                int randomY = 5 + randomizer.nextInt(100);
                Rectangle shape = new Rectangle();
                shape.setBounds(randomX, randomY, 30, 20);
                shapes.add(shape);
                undoPanel.repaint();

            }
        });

        JButton addEllipseButton = new JButton("Ellipse");

        addEllipseButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {

                int randomX = 5 + randomizer.nextInt(100);
                int randomY = 5 + randomizer.nextInt(100);
                Ellipse2D shape = new Ellipse2D.Double(randomX, randomY, 80, 30);
                shapes.add(shape);
                undoPanel.repaint();

            }
        });

        JButton undoButton = new JButton("Undo");

        undoButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {

                if (!shapes.isEmpty()) {

                    shapes.remove(shapes.size() - 1);
                    undoPanel.repaint();

                }

            }
        });

        bar.add(addRectangleButton);
        bar.add(addEllipseButton);
        bar.add(Box.createHorizontalGlue());
        bar.add(undoButton);

        frame.add(bar, BorderLayout.NORTH);
        frame.add(undoPanel, BorderLayout.CENTER);

        frame.setSize(400, 200);

        frame.setVisible(true);
    }

    public static void main(final String[] args) {

        new DrawingUndo();
    }

    private class UndoPanel extends JPanel {

        @Override
        public void paintComponent(final Graphics g) {

            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g;
            for (Shape shape : shapes) {

                g2d.draw(shape);// or g2d.fill(shape) to have its interior filled
            }
        }

    }

}

Note that Shape doesn't have things like a color for instance.

If you need such things, create a wrapper class containing the Shape and other desired parameters, and use a list of this kind of objects instead of simple Shape objects.




回答2:


As @fujy said, take a look at the Command design pattern (https://sourcemaking.com/design_patterns/command). The high level idea is that every time you want to change something, you create a command. Each command has two methods do and undo. After executing it, you should store the command in a queue (or some other data structure). That way by traversing the command chronologically, you can perform undo and implement the button. The command object should contain all information how to perform the action and how to redo it. For instance it should contain newFieldValue, oldFieldValue... There are other ways how to implement the undu concept, but I find this one very simple and generic - it doesn't depend on the technology you are using.



来源:https://stackoverflow.com/questions/44700194/undo-button-for-drawings-in-a-jframe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!