Java 如何使用重载构造函数但保持在同一JFrame中?
我试着做两件事。一个是要求用户按空格键开始,另一个是启动游戏。我试图有两个构造器,一个要求用户按下空格键,另一个启动游戏。问题是,如果我创建两个讲师,我会得到两个不同的帧,而不是一个同时包含这两个帧的帧Java 如何使用重载构造函数但保持在同一JFrame中?,java,swing,Java,Swing,我试着做两件事。一个是要求用户按空格键开始,另一个是启动游戏。我试图有两个构造器,一个要求用户按下空格键,另一个启动游戏。问题是,如果我创建两个讲师,我会得到两个不同的帧,而不是一个同时包含这两个帧的帧 import javax.swing.*; import java.awt.*; import java.text.DecimalFormat; public class DuckHunt extends JPanel { private ImageIcon imgBackgroun
import javax.swing.*;
import java.awt.*;
import java.text.DecimalFormat;
public class DuckHunt extends JPanel {
private ImageIcon imgBackground, imgForeground, imgCursor;
private Cursor cursor;
private int score, hits;
private double accuracy;
private DecimalFormat df;
private Font f;
private static final int PANEL_WIDTH = 640;
private static final int PANEL_HEIGHT = 480;
public static void main(String[] args) {
new DuckHunt();
}
public DuckHunt(String text) {
// THIS IS WHERE IM TRYING TO DO THE SPACEBAR THING
// how do I do it so it's all in one frame, instead of two seperate ones
}
public DuckHunt() {
df = new DecimalFormat("#%");
f = new Font("Neuropol", Font.BOLD, 18);
imgBackground = new ImageIcon("images\\background.png");
imgForeground = new ImageIcon("images\\foreground.png");
imgCursor = new ImageIcon("images\\cursor.png");
cursor = Toolkit.getDefaultToolkit().createCustomCursor(imgCursor.getImage(),
new Point(imgCursor.getIconWidth() / 2, imgCursor.getIconHeight() / 2), "");
setLayout(null);
setCursor(cursor);
setFocusable(true);
requestFocus();
JFrame frame = new JFrame();
frame.setContentPane(this);
frame.setTitle("Duck Hunt © Nintendo 1985");
frame.setSize(PANEL_WIDTH, PANEL_HEIGHT);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(false);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(imgBackground.getImage(), 0, 0, this);
g2.setFont(f);
g2.setColor(new Color(128, 208, 16));
g2.drawImage(imgForeground.getImage(), 0, 0, this);
g2.drawString("SCORE: " + score, 20, PANEL_HEIGHT - 50);
g2.drawString("HITS: " + hits, 250, PANEL_HEIGHT - 50);
g2.drawString("ACCURACY: " + df.format(accuracy), 450, PANEL_HEIGHT - 50);
}
}
我会添加更多的层来产生你想要的效果,包括
- 从JFrame中提取游戏JPanel
- 创建更多JPanel,包括一个用于简介视图的JPanel
- 将这些单独的JPanel显示为单独的“视图”
- 这可以通过在模态JDialog(甚至是JOptionPane)中显示简介JPanel来实现,并且在主JFrame中显示主游戏JPanel(仅在模态对话框不再可见后显示)
- 或者最好(我认为)通过CardLayout交换视图。这允许您在同一顶层窗口中交换卡组件“视图”。您还需要另一个JPanel来使用CardLayout作为布局管理器,然后使用JPanel将您的简介JPanel和游戏JPanel添加到此卡布局中。可以在以下位置找到教程:
- 在下面的示例中,我使用了PropertyChangeListener。Swing组件在默认情况下允许使用它,所以为什么不利用它呢。简介的JButton只不过是通过调用组件的属性更改支持上的
来通知任何侦听器它已被按下。然后在主GUI中,我监听这个属性更改,并通过在CardLayout中交换JPanel来响应它firePropertyChange(…)
或者,也可以使用上面相同的代码创建最初使用模态JDialog再次请求的两窗口场景,该场景可能类似于:
private static void createAndShowGui2() {
JPanel introPanel = new DuckHuntIntro();
JDialog dialog = new JDialog((JFrame)null, "Duck Hunt", true);
introPanel.addPropertyChangeListener(pcEvent -> {
if (pcEvent.getPropertyName().equals(NEXT_CARD)) {
// make dialog go away
dialog.setVisible(false);
}
});
introPanel.setPreferredSize(new Dimension(500, 300));
dialog.add(introPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
// since the dialog is modal, all code flow stops here
// until dialog is no longer visible
JPanel gamePanel = new DuckHuntGame();
gamePanel.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
JFrame frame = new JFrame("Duck Hunt © Nintendo 1985");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui2());
}
您可能会问,为什么我在使用属性更改侦听器时遇到麻烦,而不是让intro JPanel调用主GUI中的方法,告诉它交换视图。这样做的主要原因是,它减少了不必要的“耦合”,减少了多个类之间不必要的连接,这使代码更安全,更不容易出现错误,并且更容易扩展大小。例如,因为我这样做了,所以很容易将使用CardLayout的原始代码转换为使用模态JDialog的第二位代码,因为intro JPanel与主GUI没有直接连接,不知道侦听器在收到状态更改的通知后会做什么,最重要的是,不需要知道这些信息。不要为此使用两个构造函数。问题结束了。相反,在主方法中,调用一个窗口(可能是模态JDialog),然后在从JDialog返回后创建并显示主JFrame GUI。另一个可能更好的选项是:使用CardLayout交换视图--先按空格键视图,然后再按游戏。请查看要回答的编辑。如果有任何令人困惑的地方,请提问。同样,请参阅编辑以回答。
@SuppressWarnings("serial")
class DuckHuntIntro extends JPanel {
private JButton startButton = new JButton(new StartAction("Press Button to Start"));
public DuckHuntIntro() {
startButton.setFont(startButton.getFont().deriveFont(Font.BOLD, 40f));
setLayout(new GridBagLayout());
add(startButton);
}
private class StartAction extends AbstractAction {
public StartAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
@Override
public void actionPerformed(ActionEvent e) {
DuckHuntIntro.this.firePropertyChange(DuckHunt2.NEXT_CARD, null, DuckHunt2.NEXT_CARD);
}
}
}
@SuppressWarnings("serial")
class DuckHuntGame extends JPanel {
// game code goes here
public DuckHuntGame() {
JLabel dummyLabel = new JLabel("Your Main Game GUI Goes Here");
dummyLabel.setFont(dummyLabel.getFont().deriveFont(Font.PLAIN, 16f));
add(dummyLabel);
}
}
private static void createAndShowGui2() {
JPanel introPanel = new DuckHuntIntro();
JDialog dialog = new JDialog((JFrame)null, "Duck Hunt", true);
introPanel.addPropertyChangeListener(pcEvent -> {
if (pcEvent.getPropertyName().equals(NEXT_CARD)) {
// make dialog go away
dialog.setVisible(false);
}
});
introPanel.setPreferredSize(new Dimension(500, 300));
dialog.add(introPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
// since the dialog is modal, all code flow stops here
// until dialog is no longer visible
JPanel gamePanel = new DuckHuntGame();
gamePanel.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
JFrame frame = new JFrame("Duck Hunt © Nintendo 1985");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui2());
}