4

I have a program that lets the user add rectangles and circles to JPanel using Graphics. What I want to be able to do is save the current state of the current JPanel (i.e. all of the shapes and their locations) into a file and be able to load that file back and restore that state. I have a Shapes class that extends JPanel and does all of the drawing and keeps track of the shapes with an ArrayList.

Will I be able to just simply save the state of the panel? Or will I have to just save the Shapes data into a file and redraw the shapes when a file is "opened"?

Can anyone guide me on how I can save the current state of my JPanel and re-open it? Thanks

public class UMLEditor {

    public static void main(String[] args) {

        JFrame frame = new UMLWindow();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(30, 30, 1000, 700);
        frame.getContentPane().setBackground(Color.white);
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }
}

class UMLWindow extends JFrame {
    Shapes shapeList = new Shapes();

    public UMLWindow() {
        addMenus();
    }

    public void addMenus() {

        getContentPane().add(shapeList);

        JMenuBar menubar = new JMenuBar();
        JMenu file = new JMenu("File");
        JMenuItem openMenuItem = new JMenuItem("Open File");
        openMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                // Open saved state
        });

        JMenuItem saveMenuItem = new JMenuItem("Save");
        saveMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                // Save current state
            }
        });

        file.add(openMenuItem);
        file.add(saveMenuItem);

        JMenu shapes = new JMenu("Shapes");
        file.setMnemonic(KeyEvent.VK_F);

        JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
        rectangleMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                shapeList.addSquare(100, 100);
            }
        });

        JMenuItem circleMenuItem = new JMenuItem("New Circle");
        circleMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                shapeList.addCircle(100, 100);
            }
        });

        shapes.add(rectangleMenuItem);
        shapes.add(circleMenuItem);

        menubar.add(file);
        menubar.add(shapes);

        setJMenuBar(menubar);

        setSize(300, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

}

// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
class Shapes extends JPanel {
    private static final long serialVersionUID = 1L;

    private List<Path2D> shapes = new ArrayList<Path2D>();
    int currentIndex;

    public Shapes() {
        MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
        addMouseListener(myMouseAdapter);
        addMouseMotionListener(myMouseAdapter);
    }

    public void addSquare(int width, int height) {
        Path2D rect2 = new Path2D.Double();
        rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
                - height / 2, width, height), true);

        shapes.add(rect2);
        repaint();

    }

    public void addCircle(int width, int height) {
        Path2D rect2 = new Path2D.Double();
        rect2.append(new Ellipse2D.Double(getWidth() / 2 - width / 2,
                getHeight() / 2 - height / 2, width, height), true);

        shapes.add(rect2);
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        this.setOpaque(true);
        this.setBackground(Color.WHITE);
        Graphics2D g2 = (Graphics2D) g;
        for (Path2D rect : shapes) {
            g2.draw(rect);
        }
    }

    class MyMouseAdapter extends MouseAdapter {
        private boolean pressed = false;
        private Point point;

        @Override
        public void mousePressed(MouseEvent e) {
            if (e.getButton() != MouseEvent.BUTTON1) {
                return;
            }
            for (int i = 0; i < shapes.size(); i++) {
                if (shapes.get(i) != null
                        && shapes.get(i).contains(e.getPoint())) {
                    currentIndex = i;
                    pressed = true;
                    this.point = e.getPoint();
                }
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (pressed) {
                int deltaX = e.getX() - point.x;
                int deltaY = e.getY() - point.y;
                shapes.get(currentIndex).transform(
                        AffineTransform.getTranslateInstance(deltaX, deltaY));
                point = e.getPoint();
                repaint();
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            pressed = false;
        }
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Harry
  • 772
  • 10
  • 32
  • 1
    does the Shape have coordinates ? if yes then Serialize the ArrayList and save , then load and draw . – vlatkozelka Sep 28 '14 at 20:25
  • It does not, but I suppose I can implement that if there is no other way – Harry Sep 28 '14 at 20:26
  • 1
    hmm... u can instead of drawing on the JPanel , draw onto a BufferedImage , and display the image in a JLabel ... and its easier to save an image . And btw when u move the Frame , the drawing on the JPanel will get lost , but on a JLabel it wouldn't – vlatkozelka Sep 28 '14 at 20:27
  • Yes, but after I load that image I should be able to click and drag and manipulate the shapes again – Harry Sep 28 '14 at 20:28
  • 1
    Then yeah , add coordinates to the shapes , and save that arraylist – vlatkozelka Sep 28 '14 at 20:29
  • 3
    Then you'll need to save the logical entities and not an image. There are many ways to do this, including serialization or xml serialization such as with JAXB. – Hovercraft Full Of Eels Sep 28 '14 at 20:30
  • @HovercraftFullOfEels which do you prefer for my situation? I have no experience with Serialization, any helpful links/tutorials/guides? Thanks – Harry Sep 28 '14 at 20:31
  • 1
    http://www.tutorialspoint.com/java/java_serialization.htm – vlatkozelka Sep 28 '14 at 20:32
  • 2
    @Harry Serialization is meant for the short term storage of the objects, typically for the transmission across the wire. JAXB would be a preferable solution as its more flexible and you can gain some control over it. You may even need to consider using your own process, extracting the properties you need and saving them to an XML file... – MadProgrammer Sep 28 '14 at 20:34
  • 1
    Myself, I prefer to serialize something like this into a readable format, so I'd go with JAXB or some other XML serialization library if it can work with this code. – Hovercraft Full Of Eels Sep 28 '14 at 20:35
  • @MadProgrammer ok, I'll look into JAXB. I plan to use this for saving files with it's own extension (like saving a .docx in Word). Hopefully I can get her working! – Harry Sep 28 '14 at 20:36
  • 1
    @HovercraftFullOfEels *"There are many ways to do this, including serialization or xml serialization such as.."* With a handful of beans (not even magic ones) we can do it with `XMLDecoder`/`XMLEncoder`. See my answer below. – Andrew Thompson Oct 07 '14 at 15:11
  • @MadProgrammer *"extracting the properties you need and saving them to an XML file.."* With a handful of beans (not even magic ones) we can do it with `XMLDecoder`/`XMLEncoder`. See my answer below. – Andrew Thompson Oct 07 '14 at 15:13

1 Answers1

0

You could add a line of code in addCircle and addSquare that stores their H and W (or instances of the shapes themselves) in a serialized array (which could be saved to a .dat file). Then read the file and shapes.add(tehShape) for each shape entry in the dat file and repaint when needed. Used this method on an android app when i needed a persistent storage of customized ListView items. Not pretty, but it worked like a charm once properly set up. Upside of this is that you will be saving identical object instances in the dat file, downside is that they wont be human-readable.

  • The problem is that I have Path2D objects that I am drawing. Therefore, I have different shapes for each Path2D. If I implement your method I think I would need create a class for each shape and have an ArrayList for each type of shape, not just every Path2D, because Path2D isn't serialized – Harry Sep 29 '14 at 15:31