如何使用重载构造函数但保留在同一个JFrame中?

时间:2017-05-24 14:35:26

标签: java swing

我正在尝试做两件事。一个要求用户按空格键启动,一个启动游戏。我想要有两个构造函数,一个要求用户按空格键,一个启动游戏。问题是如果我创建两个教师,我会得到两个不同的帧,而不是一个帧同时具有这两个帧。

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);
    }
}

1 个答案:

答案 0 :(得分:2)

我会添加更多图层来产生你想要的效果,包括

  • 从JFrame中提取您的游戏JPanel
  • 创建更多JPanel,包括一个用于简介视图
  • 将这些单独的JPanels显示为单独的"视图"
  • 这可以通过在模态JDialog中显示JPanel,甚至是JOptionPane,以及仅在模态对话框不再可见后显示的主JFrame中的主游戏JPanel来完成。
  • 或者更好(我认为)通过CardLayout交换视图。这允许您交换卡组件"视图"在同一顶级窗口中。您需要另一个JPanel才能使用CardLayout作为其布局管理器,然后使用JPanel将您的介绍JPanel和游戏JPanel添加到此卡布局中。可以在此处找到该教程:CardLayout tutorial
  • 在下面的示例中,我使用了PropertyChangeListener。 Swing组件是有线的,默认允许使用它,所以为什么不利用它。介绍的JButton只是通过在组件的属性更改支持上调用firePropertyChange(...)来通知任何侦听器它已被按下。然后在主GUI中,我监听这个属性更改并通过在CardLayout中交换JPanel来响应它。

例如:enter image description here

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

@SuppressWarnings("serial")
public class DuckHunt2 extends JPanel {
    public static final String NEXT_CARD = "next card";
    private static final int PANEL_WIDTH = 640;
    private static final int PANEL_HEIGHT = 480;
    private CardLayout cardLayout = new CardLayout();
    private DuckHuntIntro intro = new DuckHuntIntro();
    private DuckHuntGame game = new DuckHuntGame();

    public DuckHunt2() {
        setLayout(cardLayout);
        NextCardListener nextCardListener = new NextCardListener();
        intro.addPropertyChangeListener(nextCardListener);
        add(intro, intro.getClass().toString());
        add(game, game.getClass().toString());
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PANEL_WIDTH, PANEL_HEIGHT);
    }

    private class NextCardListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals(NEXT_CARD)) {
                cardLayout.next(DuckHunt2.this);
            }
        }
    }


    private static void createAndShowGui() {
        DuckHunt2 mainPanel = new DuckHunt2();

        JFrame frame = new JFrame("Duck Hunt © Nintendo 1985");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

@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);
    }
}

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

您可能会问为什么我遇到了使用属性更改侦听器的麻烦,而不是让介绍JPanel调用主GUI中保存的方法来告诉它交换视图。这样做的主要原因是它减少了不必要的耦合",多个类之间不必要的连接,这使您的代码更安全,更容易出错,并且更容易扩展。例如,因为我这样做,很容易将使用CardLayout的原始代码转换为使用模态JDialog的第2位代码,因为介绍JPanel没有直接连接到主GUI,不知道听众会是什么做一次状态更改通知,最重要的是,没有需要知道这些信息。