Andrew Martin
Andrew Martin

Reputation: 5741

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:

What I'm trying to fix:

Questions:

Upvotes: 3

Views: 7144

Answers (2)

David Kroukamp
David Kroukamp

Reputation: 36423

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

Upvotes: 4

Qwerky
Qwerky

Reputation: 18445

When you handler the door opening event with your listener;

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

you don't actually include a call to repaint the panel; hence the panel's paintComponent() method isn't called and door.drawOpenDoor() isn't called. You can test this by clicking the button and then resizing the frame. When you resize, the panel is automatically repainted and bingo, your door appears.

You can fix this by adding a call to repaint() in your ActionListener;

class openDoorListener implements ActionListener {
  public void actionPerformed(ActionEvent event) {
    door.open();
    repaintText();
    repaint();   // requests that the panel be repainted
  }
}

Upvotes: 3

Related Questions