Skip to content
Advertisement

JPanel not showing in JFrame, but JFrame still changes size

I don’t know what I did, or what went wrong, but a change I made at some point in the last while has made my JPanel completely invisible. The JFrame it’s nested in still changes in size to house it, and I can still toggle the content in the combobox.

In my desperation, I tried replacing the content of the SnakeSettingsPanel class with a single button, but the same thing happened – completely invisible, yet I can still interact with it. I figured it might be a computer error, so I tried restarting, but still nothing. When I tried adding a button to the frame outside of the JPanel, it worked just fine. What am I doing wrong?

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class SnakeSettingsPanel extends JPanel {
    public boolean quit = false;
    public boolean play = false;
    public int width = 20;
    public int height = 15;
    public Speed speed = Speed.SLOW;

    public JTextField wField;
    public JTextField hField;
    public JComboBox<Speed> sField;

    public static void main(String[] args) {
        JFrame jf = new JFrame();
        jf.setTitle("Snake");
        SnakeSettingsPanel settings = new SnakeSettingsPanel();
        jf.add(settings);
        jf.pack();
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public SnakeSettingsPanel() {
        setLayout(new GridBagLayout());

        // @author Create our labels.
        JLabel wLabel = new JLabel("Width:");
        JLabel hLabel = new JLabel("Height:");
        JLabel sLabel = new JLabel("Speed:");
        
        GridBagConstraints p = new GridBagConstraints();
            p.gridx = 0;
            p.gridy = 0;
            p.insets = new Insets(10, 10, 10, 10);

        // @author Create the buttons, and add listeners
        JButton y = new JButton("Play");
        JButton n = new JButton("Quit");
        y.addActionListener(new PlayListener());
        n.addActionListener(new QuitListener());

        // @author Create text fields for height/width
        wField = new JTextField(15);
        wField.setText("20");
        hField = new JTextField(15);
        hField.setText("15");

        // @author Creates a combobox for selecting speed.
        Speed[] speeds = {Speed.SLOW, Speed.MEDIUM, Speed.FAST};
        sField = new JComboBox<Speed>(speeds);

        // @author Stitch everything into the panel.
        add(wLabel, p);
        p.gridx = 1;
        add(wField, p);
        p.gridx = 0;
        p.gridy = 1;
        add(hLabel, p);
        p.gridx = 1;
        add(hField, p);
        p.gridx = 0;
        p.gridy = 2;
        add(sLabel, p);
        p.gridx = 1;
        add(sField, p);
        p.gridx = 0;
        p.gridy = 3;
        add(y, p);
        p.gridx = 1;
        add(n, p);

        setVisible(true);
    }

    public boolean getPlay() {
        return play;
    }

    public boolean getQuit() {
        return quit;
    }

    // @author Returns all settings as a SnakeSettings object
    public SnakeSettings getSettings() {
        return new SnakeSettings(width, height, speed);
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public Speed getSpeed() {
        return speed;
    }

    // @author Sends out the word to start a new game.
    public class PlayListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            quit = false;
            play = true;
            width = Integer.parseInt(wField.getText());
            height = Integer.parseInt(hField.getText());
            speed = (Speed) sField.getSelectedItem();
        }
    }

    // @author Sends out the word to shut down the program.
    public class QuitListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            quit = true;
            play = false;
        }       
    }
}

Advertisement

Answer

Let this be a lesson on why you should avoid mixing Model (your application’s data) with View (how it is displayed). Your SnakeSettingsPanel is currently both.

  • As Model, it contains 3 important fields: width, height, and speed.
  • As View, it is a full JPanel. JPanels have a lot of fields which you should avoid touching directly. Including width and height, usually accessed via getHeight and getWidth — which you are overwriting with a version that always returns the same built-in values of 20 and 15 (until the user changes those values through a UI that they cannot see).

The fast fix is to rename your current getWidth() and getHeight() to avoid clashing with the built-in getWidth() and getHeight() methods of the parent JPanel class. Call them getMyWidth(), getMyHeight(), and suddenly everything works.

The better fix is to remove those fields and methods entirely, and store your own model attributes in a SnakeSettings attribute. Update it when the user clicks on play, and return it when it is requested via getSettings(). Less code for you, less chance of accidental name clashes with your parent JPanel class. This would look like:

    // SnakeSettingsPanel, before
    public int width = 20;
    public int height = 15;
    public Speed speed = Speed.SLOW;

    public int getWidth() {    // <-- clashes with superclass
        return width;
    }
    public int getHeight() {   // <-- clashes with superclass
        return height;
    }
    public Speed getSpeed() {
        return speed;
    }
    public SnakeSettings getSettings() {
        return new SnakeSettings(width, height, speed);
    }

    // SnakeSettingsPanel, after
    SnakeSettings settings = new SnakeSettings();

    public SnakeSettings getSettings() {
        return settings;
    }
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement