0

I am attempting to make a simple traffic light frame. This is being made by the main frame TrafficBox:

public class TrafficBox extends JFrame {

public TrafficBox() {
    setLayout(new BorderLayout(100,100));
    add(new TrafficLight(Color.GREEN), BorderLayout.NORTH);
    add(new TrafficLight(Color.ORANGE), BorderLayout.CENTER);
    add(new TrafficLight(Color.RED), BorderLayout.SOUTH);
    setBounds(100, 100, 800, 600);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setVisible(true);
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new TrafficBox();
        }
    });

}

which then as seen, adds in 3 TrafficLight components which are based on JPanel, and have the following code:

public class TrafficLight extends JPanel {

private final int BALL_DIAMETER = 100;
private Color color;

public TrafficLight(Color color) {
    //setSize(BALL_DIAMETER, BALL_DIAMETER);
    this.color = color;
    new Timer(1000, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            repaint();
        }
    }).start();
}

public void paintComponent(Graphics g) {
    g.setColor(color);
    g.fillOval(0, 0, BALL_DIAMETER, BALL_DIAMETER);
}

This does sort of what I want, it draws all 3 circles as expected although a majority of the North(green) and south(red) lights are being cut off. I assume this is because the north/south spot are much smaller than the center.

enter image description here

I've tried using setSize(); to set the size of the panels to the size of the circles, however that does not work. Is there a way I can make it so that the full circle will be visible?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433

2 Answers2

3

So, most layout managers will need some "hints" to be provided by the components in order to know how they want to be laid out.

You will need to override the getPreferredSize and return a size which best meets your needs, for example...

public class TrafficLight extends JPanel {

    private final int BALL_DIAMETER = 100;

    //...

    public Dimension getPreferredSize() {
        return new Dimension(BALL_DIAMETER, BALL_DIAMETER);
    }    

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent();
        g.setColor(color);
        g.fillOval(0, 0, BALL_DIAMETER, BALL_DIAMETER);
    }

Also, paintComponent doesn't ever need to be public no-one else should be calling it and you should call super.paintComponent() before performing any custom painting.

I'd also recommend maybe using GridLayout for this

I'd also argue that TrafficLight doesn't need a Timer of it's own and the should be controlled externally, but that's me

setBounds(100, 100, 800, 600);

Is best avoided, use pack() to size the window to the preferred size of the content and setLocation to position it

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    The only thing I can add to this is that once `getPreferredSize()` is overridden on the `TrafficLight` component, the best way to size the frame is to (add all the components) then call the `pack()` method of the frame. – Andrew Thompson Oct 25 '18 at 03:30
0

Simple fix, needed to be using setPreferredSize() rather than setSize.

  • 1
    `setPreferredSize` is not a good idea - to easy for some one else to screw with you - see [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](https://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi) for more details – MadProgrammer Oct 25 '18 at 03:21
  • This is not the proper solution. Don't forget to "accept" madProgrammers solution by clicking on the checkmark since this is the proper solution. In fact don't forget to accept answers in your previous questions. – camickr Oct 25 '18 at 03:44