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只不过是通过调用组件的属性更改支持上的
    firePropertyChange(…)
    来通知任何侦听器它已被按下。然后在主GUI中,我监听这个属性更改,并通过在CardLayout中交换JPanel来响应它
例如:


或者,也可以使用上面相同的代码创建最初使用模态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());
}