Search code examples
javaswingjframejpanelpaintcomponent

Adding graphics into a JFrame using BorderLayout


I'm trying to do a simple piece of homework, where I display a line of text displaying whether a door object is open or not. Underneath that, I visually represent it (using the drawRect) method. And at the bottom I have two buttons, which can open or close the door, thus changing the text and rectangle.

Edit: List of code that can be compiled given now:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;

    public class Test {

    public static void main(String[] args) {

        // Creates new JFrame called frame, with title "Door" 
        // (displayed at top of screen).
        JFrame frame = new JFrame ("Door");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        TempDoorPanel panel = new TempDoorPanel();
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);

        }
    }

    class Door {

    private String state;
    private String message;

    Door (String state) {
        this.state = state;
        message = "The door is currently closed.";
    }

    public boolean isOpen() {
        return state.equals ("open");
    }

    public boolean isClosed() {
        return state.equals ("closed");
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getMessage() {
        return message; 
    }

    public void open() {
        if (state.equals("open")) {
            message = "The door is already open.";
        }
        else {
            state = "open";
            message = "The door has been opened.";
        }
    }

    public void drawOpenDoor (Graphics page) {
        page.drawRect(100, 100, 100, 100);
    }
    }

    class TempDoorPanel extends JPanel {

    private Door door;
    private JTextField currentStateOfDoor;
    private JButton openDoor;

    public TempDoorPanel() {
        super.setLayout(new BorderLayout());
        door = new Door("closed");
        super.setBackground(Color.blue);
        super.setPreferredSize(new Dimension (360, 400));

        currentStateOfDoor = new JTextField(14);
        currentStateOfDoor.setText(door.getMessage());
        super.add(currentStateOfDoor, BorderLayout.NORTH);

        openDoor = new JButton("Open Door");

        class openDoorListener implements ActionListener {
            public void actionPerformed (ActionEvent event) {
                door.open();
                repaintText();
            }
        }

        openDoorListener openlistener = new openDoorListener();
        openDoor.addActionListener(openlistener);

        JPanel holder = new JPanel();
        holder.add(openDoor);
        super.add(holder, BorderLayout.SOUTH);
    }

    private void repaintText() {
        currentStateOfDoor.setText(door.getMessage());
        // These methods are from Door class.
    }

    public void paintComponent (Graphics page) {
        super.paintComponent(page);
        if (door.isOpen())
            door.drawOpenDoor(page);
        // isOpen is a boolean method from Door class.
    }
}

What works:

  • Buttons appear at right place on screen, at BorderLayout.SOUTH, one after the other.
  • The JTextField appears at right place, at BorderLayout.NORTH
  • Finally, the blue area appears in the right place in the centre of the screen.

What I'm trying to fix:

  • I have no idea how to display the rectangle properly in the middle of that blue area. I've tried changing the coordinates and size of the rectangle, which doesn't change the size of it at all. I can make it drawRect(100, 100, 100, 100) and it changes nothing.
  • I'm also aware that the rectangle is currently hidden behind the top left corner of the JTextField, but I can't figure out how to move it into the BorderLayout.

Questions:

  • How do you place a rectangle in a BorderLayout?
  • How do you adjust the size of a rectangle, drawn via drawrect(), in such a layout?

Solution

  • Because you add components to the JPanel you draw on the JTextField is covering your drawing.

    Solution:

    1) Either compensate for this by checking the JTextField height in your drawRect(..) method

    or better

    2) Dont add components to the same JPanel which you are drawing on unless it cant be helped.

    So basically I made your TempDoorPanel add a new JPanel to BorderLayout.CENTER which is the drawing panel we can now use drawRect(0,0,10,10) and it will show in the top left hand corner of JPanel drawingPanel.

    • Also dont call setPreferredSize on JPanel rather override getPreferredSize() and return Dimensions which fit your drawings.

    • To invoke paintComponent outside of the class simply call repaint() its instance

    See this example which uses point no.2:

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    public class Test {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame("Door");
                    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    
                    TempDoorPanel panel = new TempDoorPanel();
                    frame.add(panel);
                    frame.pack();
                    frame.setVisible(true);
                }
            });
        }
    }
    class Door {
    
        private String state;
        private String message;
    
        public Door(String state) {
            this.state = state;
            message = "The door is currently closed.";
        }
    
        public void drawOpenDoor(Graphics page) {
            page.setColor(Color.GREEN);
            page.drawRect(0, 0, 10, 10);
        }
    }
    
    class TempDoorPanel extends JPanel {
    
        private Door door;
        private JTextField currentStateOfDoor;
        private JButton openDoor;
    
        public TempDoorPanel() {
            super.setLayout(new BorderLayout());
            door = new Door("closed");
    
            currentStateOfDoor = new JTextField(14);
            //AcurrentStateOfDoor.setText(door.getMessage());
            super.add(currentStateOfDoor, BorderLayout.NORTH);
    
            openDoor = new JButton("Open Door");
    
            final JPanel drawingPanel = new JPanel() {
                @Override
                protected void paintComponent(Graphics grphcs) {
                    super.paintComponent(grphcs);
                    // if (door.isOpen()) {
                    door.drawOpenDoor(grphcs);
                    // }
                    // isOpen is a boolean method from Door class.
    
                }
            };
            drawingPanel.setBackground(Color.blue);
            add(drawingPanel);
    
            class openDoorListener implements ActionListener {
    
                public void actionPerformed(ActionEvent event) {
                    //door.open();
                    repaintText();
                    drawingPanel.repaint();//so paint component of drawing panel is called
                }
            }
    
            openDoorListener openlistener = new openDoorListener();
            openDoor.addActionListener(openlistener);
    
            JPanel holder = new JPanel();
            holder.add(openDoor);
            super.add(holder, BorderLayout.SOUTH);
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(300, 300);
        }
    
        private void repaintText() {
            // currentStateOfDoor.setText(door.getMessage());
            // These methods are from Door class.
        }
    }