0

I've been searching for a solution for a while now and despite all the similar questions and answers, found nothing that seems to work. I want a user to be able to progress through various panels set up in a card layout. However, I want the buttons used to switch between these cards to be on the cards themselves, not a separate set of buttons which doesn't change throught the program. Here is the main file where the frame is created:

public class BattleGraphs_V1 
{
    JPanel cards;

    public int cardNumber = 1;

    public void addComponentToPane (Container pane)
    {
        JPanel MainMenuCard = new MainMenu_V1();
        JPanel DifficultySelectorCard = new DifficultySelector_V1();

        cards = new JPanel(new CardLayout());

        cards.add(MainMenuCard);
        cards.add(DifficultySelectorCard);

        pane.add(cards, BorderLayout.CENTER);
    }

    private static void createAndShowGUI() 
    {
        JFrame frame = new JFrame("BattleGraphs");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        frame.setPreferredSize(new java.awt.Dimension(725, 420));
        frame.setResizable(false);

        BattleGraphs_V1 containerPanel = new BattleGraphs_V1();
        containerPanel.addComponentToPane(frame.getContentPane());

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) 
    {
        javax.swing.SwingUtilities.invokeLater(new Runnable() 
        {
            public void run() 
            {
                createAndShowGUI();
            }
        });
    }
}

The two panels (more in the future) are in two seperate files in the project. I haven't added the code for them as apart from an action listener for the single button on each panel, it was all auto-generated code.

EDIT: I added an auto-generated action listener to the button to the MainMenu JPanel like so:

private void MainMenuCardSBActionPerformed(java.awt.event.ActionEvent evt) {                                               
        BattleGraphs_V1 BG_V1 = new BattleGraphs_V1();
        CardLayout cardLayout = (CardLayout) (BG_V1.cards.getLayout());
        cardLayout.show(BG_V1.cards, "DifficultyCard");
    }

After having added names to the cards added in the main file (main file being BattleGraphs_V1) like so:

cards.add(MainMenuCard, "MainMenuCard");
cards.add(DifficultySelectorCard, "DifficultyCard");

And when I ran the program and clicked the button got a null pointer exception error, as I have done with similar solutions. Would I be wrong in assuming this is more down to an issue of scope?

2nd EDIT: Right, hopefully this should help a little, I've added the enite project to a GitHub repository - https://github.com/charga600/BattleGraphs/tree/master

charga600
  • 1
  • 2
  • 1
    https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html – Aubin Jan 25 '17 at 12:19
  • Which line gives you the NPE? – matt Jan 25 '17 at 15:50
  • Also, you shouldn't be creating a `new BattleGraphs` you should have a reference to the one you want, otherwise you never initialize BG_V1.cards. So that is probably the line giving you the NPE. – matt Jan 25 '17 at 15:53
  • Here's a question, what responsibility does the card/panel have to determine the next navigation point? Answer, none, why? Because modifying the navigation in this way is a nightmare. Solution? Have a model/controller which can be asked to navigate to the next step from the panel – MadProgrammer Jan 25 '17 at 22:53
  • I don't think you should be creating a new instance of `BattleGraphs_V1` in your `ActionListener`, this is, yet, another reason why you shouldn't be doing it this way, the panel should be isolated from having to make these decisions as it's making assumptions about how it's been setup – MadProgrammer Jan 25 '17 at 22:55
  • As an [example](http://stackoverflow.com/questions/36971360/button-in-cardlayout-not-working/36972129#36972129) and for a "really long" [example of how to use a MVC](http://stackoverflow.com/questions/31602113/listener-placement-adhering-to-the-traditional-non-mediator-mvc-pattern/31604919#31604919) :P – MadProgrammer Jan 25 '17 at 22:57
  • @MadProgrammer The setup you have in the first link is something around what I'm looking for design/function wise. Will it work if the JPanels were created in seperate files like I have? If so, how! :P (Added a GitHub link for the full project) – charga600 Jan 30 '17 at 10:25
  • Yes, they're just classes, the thing that holds it all together are the interfaces – MadProgrammer Jan 30 '17 at 10:29
  • @MadProgrammer so when I'm adding the action listener to the button into the JPanel, how would I go about doing it? – charga600 Jan 30 '17 at 10:45
  • The ActionListerner is associated with panel (as an inner or anonymous class if you want), it then talks to the controller – MadProgrammer Jan 30 '17 at 19:04

1 Answers1

1

When you add your cards to your panel you should name them so you can select switch one you'd like to show on a certain event.

cards = new JPanel(new CardLayout());

cards.add(MainMenuCard, "first");
cards.add(DifficultySelectorCard, "second");

pane.add(cards, BorderLayout.CENTER);

GUI:

JButton button = new JButton("Click here");
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        CardLayout cl = (CardLayout) (cards.getLayout());
        cl.show(cards, "second");
    }
});

EDIT

This is a short example to demonstrate CardLayout, since you didnt share your full code.

Main class

    public class MainMenu_V1 extends JFrame {

    private JMenuBar menuBar;
    private JMenu file;
    private JMenuItem exit;
    private JPanel mainPanel;

    public MainMenu_V1() {
        setTitle("Main Panel");
        setResizable(false);
        setSize(new Dimension(400, 200));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setJMenuBar(createMainMenu());
        setLocationRelativeTo(null);

        mainPanel = new JPanel();
        mainPanel.setLayout(new CardLayout());
        mainPanel.add(new FirstPanel(mainPanel), "FIRST");
        mainPanel.add(new SecondPanel(mainPanel), "SECOND");

        setContentPane(mainPanel);
    }

    public JMenuBar createMainMenu() {
        menuBar = new JMenuBar();
        file = new JMenu("File");
        exit = new JMenuItem("Exit");
        exit.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });

        file.add(exit);
        menuBar.add(file);

        return menuBar;
    }

    public void switchPanel(Container container, String panelName) {
        CardLayout card = (CardLayout) (container.getLayout());
        card.show(container, panelName);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new MainMenu_V1().setVisible(true);
            }
        });
    }
}

First Panel

public class FirstPanel extends JPanel {

    private JButton button;
    private JPanel mainPanel;

    public FirstPanel(JPanel mainPanel) {
        this.mainPanel = mainPanel;
        setPreferredSize(new Dimension(400, 200));
        setBackground(Color.GRAY);
        add(createButton());        
    }   

    private JButton createButton() {
        button = new JButton("Switch to the second Panel");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MainMenu_V1 main = new MainMenu_V1();
                main.switchPanel(mainPanel, "SECOND");                
            }
        });
        return button;
    }
}

Second Panel

public class SecondPanel extends JPanel {

    private JButton button;
    private JPanel mainPanel;

    public SecondPanel(JPanel mainPanel) {
        this.mainPanel = mainPanel;
        setPreferredSize(new Dimension(400, 200));
        setBackground(Color.ORANGE);
        add(createButton());
    }   

    private JButton createButton() {
        button = new JButton("Switch to the first Panel");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MainMenu_V1 main = new MainMenu_V1();
                main.switchPanel(mainPanel, "FIRST");                
            }
        });
        return button;
    }
}
Zsolt Ébel
  • 120
  • 1
  • 9
  • Also check out this post: [cardLayout](http://stackoverflow.com/questions/33662879/switching-jpanels?answertab=votes#tab-top) – Zsolt Ébel Jan 25 '17 at 12:43
  • It's hard to tell what is the problem, we cannot see your full code. I added an example to my post. – Zsolt Ébel Jan 25 '17 at 21:05
  • I've link to a gitHub repository, should give you access to the full code without having this page as an endless scroller. – charga600 Jan 30 '17 at 10:10
  • Also when I take the example code you've supplied and create a netbeans project out of it, when I attempt to run the project it gives me this "Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Uncompilable source code - Erroneous tree type: ". With the issue stemming from line 31 - "mainPanel.add(new FirstPanel(mainPanel), "FIRST");" and 66 " new MainMenu_V1().setVisible(true);" – charga600 Jan 30 '17 at 10:33