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;
}
}
}
CodePudding user response:
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
, andspeed
. - As View, it is a full JPanel. JPanels have a lot of fields which you should avoid touching directly. Including
width
andheight
, usually accessed viagetHeight
andgetWidth
-- 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;
}