我正在构建一个Java Virtual Pet游戏,当前菜单面板工作正常,按钮正在加载和运行,但是当我单击应该启动到GamePanel面板中的新游戏按钮时,会出现nullpointerException。

这是构建原始框架和要切换的Jpanel的主要类。

public class MainFrame extends JPanel {

public JPanel mainPanel;
public CardLayout cl;
private final GamePanel gamePanel;
private final MenuPanel menuPanel;

/**
 *  Constructs the main panel to be used to switch between panels.
 *
 */
public MainFrame() {

    // creates a new panel to add panels to.
    cl = new CardLayout();

    // panel to be used as a main switch.
    mainPanel = new JPanel();
    Dimension size = getPreferredSize();
    size.width = 600;
    size.height = 600;
    setPreferredSize(size);
    setBackground(Color.BLACK);

    add(mainPanel);

    gamePanel = new GamePanel();
    menuPanel = new MenuPanel();

    // sets layout
    mainPanel.setLayout(cl);

    mainPanel.add(menuPanel, "menuPanel");
    mainPanel.add(gamePanel, "gamePanel");

}

public void changePanel(String name) {

    cl.show(mainPanel, name);

}

/**
 * Main frame used by the game.
 *
 * @param args
 */
public static void main(String[] args) {

MainFrame game = new MainFrame();

JFrame frame = new JFrame("Main Window");
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.setLocationRelativeTo(null);

}


}

这是我的MenuPanel类中的代码,应该访问changePanel()方法。

public class MenuPanel extends JPanel {

MainFrame mf;

public MenuPanel() {
    this.mf = mf;

    Dimension size = getPreferredSize();
    size.width = 600;
    size.height = 600;
    setPreferredSize(size);

    ImageIcon menuIcon = new ImageIcon("C:\\Programming\\NetBeansProjects\\PDCMain\\src\\Data\\the_menu_title2.png");
    JLabel menuLbl = new JLabel();
    menuLbl.setIcon(menuIcon);

    JButton newGameBtn = new JButton("New Game");
    JButton loadGameBtn = new JButton("Load Game");
    JButton helpBtn = new JButton("Instructions");
    JButton exitBtn = new JButton("Exit");

    newGameBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("New Game");
            mf.changePanel("gamePanel");
        }
    });

loadGameBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {

        }
    });

    helpBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            showHelp();
        }
    });

    exitBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    });


    setLayout(new GridBagLayout());

    GridBagConstraints gc = new GridBagConstraints();

    gc.weightx = 0;
    gc.weighty = 0.1;

    gc.gridx = 0;
    gc.gridy = 1;
    add(menuLbl, gc);

    gc.gridx = 0;
    gc.gridy = 2;
    add(newGameBtn, gc);

    gc.gridx = 0;
    gc.gridy = 3;
    add(loadGameBtn, gc);

    gc.gridx = 0;
    gc.gridy = 4;
    add(helpBtn, gc);

    gc.gridx = 0;
    gc.gridy = 5;
    add(exitBtn, gc);

}

public void showHelp(){
    String[] help = new String[100];
    try {
        BufferedReader br = new BufferedReader(new FileReader("instruct.txt"));
        String line = "";
        int i = 0;
        while((line = br.readLine()) != null) {
            help[i] = line;
            i++;
        }
        JOptionPane.showMessageDialog(null, help);
    } catch(IOException ex) {
        System.err.println("IOException Error: " + ex.getMessage());
    }
}


这是游戏面板

public class GamePanel extends JPanel{

private ButtonsPanel buttonsPanel;
private GraphicsPanel graphicsPanel;
private final JPanel gamePanel;

public GamePanel(){

    super();
    this._initGUI();

    gamePanel = new JPanel();
    Dimension size = getPreferredSize();
    size.width = 600;
    size.height = 600;
    setPreferredSize(size);
    setBackground(Color.RED);

}
private void _initGUI(){

    this.buttonsPanel = new ButtonsPanel();
    this.graphicsPanel = new GraphicsPanel();

    this.setLayout(new BorderLayout());
    this.add(buttonsPanel, BorderLayout.SOUTH);
    this.add(graphicsPanel, BorderLayout.CENTER);
}

public void run(){
    graphicsPanel.run();
}


}

这是我第一次尝试从头开始构建GUI并使用cardlayout。我看不到它将为空值分配的位置,因为在用于切换的主面板中声明了该面板,有什么想法吗?

最佳答案

花点时间看一下这段代码...

public static class MenuPanel extends JPanel {

    MainFrame mf;

    public MenuPanel() {
        this.mf = mf;


基本上,您是将(thismf分配给this.mf,但是由于mf已经是null,因此this.mf将保持null ...

您应该将MainFrame的引用传递给您的MenuPanel

现在,因为我讨厌将UI组件的引用传递给程序的其他部分,而这些组件应该对UI的其余部分一无所知,并且不要直接控制它们,所以我建议不要像这样传递MainFrame盲法。

而是创建一个interface,它提供您希望允许MenuPanel执行的功能(例如newGameloadGameshowInstructionsexit),并允许实现此interface (可能是MainFrame)来处理

然后,您可以将此接口的实例传递给MenuPanel ...这将类暴露给不需要这些类功能的其他类,并解耦了代码,因此您可以将接口的任何旧实例传递给该类。无需重写代码的整个部分...

更新了基本概念

从某种游戏控制器界面开始,例如...

public interface GameController {

    public void newGame();
    public void loadGame();
    public void showInstructions();
    public void exit();

}


这描述了可以针对此接口的实现执行的操作。

使用MainFrame并实现GameController ...

public class MainFrame extends JPanel implements GameController {

    //...

    public void newGame() {
    }

    public void loadGame() {
    }

    public void showInstructions() {
    }

    public void exit() {
    }


更改MenuPane以在构造时要求GameController的实例...

public class MenuPanel extends JPanel {

    private GameController controller;

    public MenuPanel(GameController gameController) {
        this.controller = gameController;
        //...


现在,当您处理用户交互时,您可以将请求传递给控制器​​。

newGameBtn.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("New Game");
        controller.newGame();
    }
});


这样,MenuPanel不知道如何创建新游戏,它只是知道它将得到照顾...

现在,您将需要更新newGame中的MainFrame方法以执行某些操作,例如...

public void newGame() {
    // Setup the new game...
    cl.show(mainPanel, "gamePanel");
}


这将所有责任与GameController的实现完全放在一起,以决定某些操作的发生方式,使过程脱钩,因此,如果将GameController的实现更改为其他类,则不需要担心更新MenuPanel,因为它只对进入GameController ... nice感兴趣;)

08-25 14:10