How to switch from a JPanel to another

101 Views Asked by At

I'm developing a game, written in java swing. I create a class MainGUI that extends JFrame, to contain the multiple screens of the application. In other classes, I create different JPanels, like StartPanel and GamePanel, that are inserted during execution in the MainGUI. In the StartPanel, when the user entered the nickname, a button allows to switch to GamePanel.

The problem is that I can't switch panels. I try to use CardLayout, but when I press a button in a panel, I can't handle the event in the MainGUI and show a different panel.

I used the MVC design pattern, so there are 3 packages (controller, model, view) that communicate with each other thanks to the classes ControllerForModel, ControllerForView, View and Model, that implement the relevant interface.

The application is started thanks to the class Main in controller package.

package frogger.view;

import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainGUI extends JFrame {

    //---------------------------------------------------------------
    // STATIC FIELDS
    //---------------------------------------------------------------
    
    public MainGUI(String title) {
        super(title);
        
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        StartPanel startPane = new StartPanel();
        
        Container contPane = this.getContentPane();
        contPane.add(startPane);

        this.pack();
    } //end constructor
    

This is the code of MainGUI class, and it only shows the StartPanel.

package frogger.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class StartPanel extends JPanel {
    
    //---------------------------------------------------------------
    // CONSTANTS FIELDS
    //---------------------------------------------------------------
    private final static String LOGO_PATH = "img/logo.png";
    
    
    //---------------------------------------------------------------
    // STATIC FIELDS
    //---------------------------------------------------------------
    private static Image logoImg;
    
    private static JLabel logoLabel;
    private static JLabel newGameLabel;
    private static JLabel loadGameLabel;
    private static JLabel newGameLabelInCurrentDisplayedPanel;

    private static JButton newGameButton;
    private static JButton loadGameButton;
    private static JButton newGameButtonInCurrentDisplayedPanel;
    
    private static JTextField nicknameTextField;
    
    private static JPanel currentDisplayedPanel;
    
    public StartPanel() {
        //todo
        super();
        this.setLayout(new GridBagLayout());
        
        loadImage(LOGO_PATH);
        this.logoLabel = new JLabel(new ImageIcon(this.logoImg.getScaledInstance(400, 200, Image.SCALE_SMOOTH)));
        locateLogoLabel();
        
        this.newGameLabel = new JLabel("Start a new game:");
        locateNewGameLabel();
        
        this.newGameButton = new JButton("New");
        this.newGameButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                handleShowNewGamePanelEvent();
            }
        });
        locateNewGameButton();
        
        this.loadGameLabel = new JLabel("Load a previously saved game:");
        locateLoadGameLabel();
        
        this.loadGameButton = new JButton("Load");
        this.loadGameButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                handleShowLoadGamePanelEvent(e);
            }
        });
        locateLoadGameButton();
        
        this.currentDisplayedPanel = new JPanel(new GridBagLayout());
        this.currentDisplayedPanel.setPreferredSize(new Dimension(400, 100));
        locateCurrentDisplayedPanel();
        
    } //end constructor

    
    //---------------------------------------------------------------
    // INSTANCE METHODS
    //---------------------------------------------------------------
    private void handleNewGameEvent() {
        //todo
    }
    
    private void handleLoadGameEvent() {
        //todo  
    }
    
    private void handleShowNewGamePanelEvent() {
        System.out.println("New game button pressed");
        
        this.newGameLabelInCurrentDisplayedPanel = new JLabel("Inserisci il tuo nickname:");
        locateNewGameLabelInCurrentDisplayedPanel();
        
        this.nicknameTextField = new JTextField(15);
        locateNicknameTextFieldInCurrentDisplayedPanel();
        
        this.newGameButtonInCurrentDisplayedPanel = new JButton("Conferma");
        this.newGameButtonInCurrentDisplayedPanel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                handleNewGameEvent();
            }
        });
        locateNewGameButtonInCurrentDisplayedPanel();
        
        
        this.currentDisplayedPanel.revalidate();
        this.currentDisplayedPanel.repaint();
        
    }
    
    private void handleShowLoadGamePanelEvent() {
        System.out.println("Load game button pressed");
    }
    
} //end class

This is the code of StartPanel.

package frogger.controller;

import frogger.view.MainGUI;

public class Main {
    
    public static void main(String[] args) {
        ControllerForView.getInstance().openGameWindow();
    }
}

And this is the code of the class Main that starts the application.

The question is: how can I switch from StartPanel to GamePanel when the user press newGameButton? In other words, how can I implement the method handleNewGameEvent() to dialogue with the MainGUI?

Thanks everyone in advance

I tried to use CardLayout in MainGUI to show a panel and another, but I don't know how to communicate to MainGUI that has to switch panel when the user clicks on a button in a panel describe with an external class.

1

There are 1 best solutions below

0
Hovercraft Full Of Eels On

As I mentioned in comment, I would use a CardLayout, and I would try to separate control of the card layout from the view, using MVC, or in my example below, a more simplified model-controller. Here, any class that has access to the controller can control what the card layout displays:

enter image description here

MainGUI.java

This creates the components, connects them, and starts them.

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;

import javax.swing.*;

public class MainGUI {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            View view = new CardView();
            CardController controller = new ConcreteController(view);
            String[] cards = { "Card 1", "Card 2", "Card 3", "Card 4", "Card 5",
                    "Card 6", "Card 7", "Card 8", "Card 9", "Card 10" };
            for (String card : cards) {
                JPanel cardPanel = new RandomPanel(card, controller);
                controller.addCard(card, cardPanel);
            }
            controller.showGui();
        });
    }
}

ConcreteController.java

This is the class that controls what the card layout displays.

class ConcreteController implements CardController {
    private View view;
    private JFrame frame;

    public ConcreteController(View view) {
        this.view = view;
        view.setContoller(this);
    }

    @Override
    public void showGui() {
        if (frame == null) {
            frame = new JFrame("MainGUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setContentPane(view.getMainPanel());
            frame.pack();
            frame.setLocationRelativeTo(null);
        }
        frame.setVisible(true);
    }

    public View getView() {
        return view;
    }

    @Override
    public void addCard(String name, JPanel card) {
        view.getCardsPanel().add(card, name);
        view.getCardComboBoxModel().addElement(name);
    }

    @Override
    public void showCard(String name) {
        view.getCardLayout().show(view.getCardsPanel(), name);
    }

    @Override
    public void nextCard() {
        view.getCardLayout().next(view.getCardsPanel());
    }

    @Override
    public void previousCard() {
        view.getCardLayout().previous(view.getCardsPanel());
    }
}
interface CardController {
    public void addCard(String name, JPanel card);

    public void showGui();

    public void showCard(String name);

    public void nextCard();

    public void previousCard();
}
interface View {

    void setContoller(CardController controller);

    CardLayout getCardLayout();

    JPanel getCardsPanel();

    JPanel getMainPanel();

    DefaultComboBoxModel<String> getCardComboBoxModel();

}

CardView.java

The GUI class that holds the cardsPanel, the one that uses a CardLayout, and that displays the cards JPanels. It also has previousButton, nextButton and a JComboBox to allow selection of the visualized card

class CardView implements View {
    private CardController controller;
    private JPanel mainPanel = new JPanel(new BorderLayout());
    private CardLayout cardLayout = new CardLayout();
    private JPanel cardsPanel = new JPanel(cardLayout);
    private DefaultComboBoxModel<String> cardComboBoxModel = new DefaultComboBoxModel<>();
    private JComboBox<String> cardComboBox = new JComboBox<>(cardComboBoxModel);

    public CardView() {
        mainPanel.add(cardsPanel, BorderLayout.CENTER);

        JPanel buttonPanel = new JPanel();

        JButton previousButton = new JButton("Previous");
        previousButton.addActionListener(e -> {
            if (controller != null)
                controller.previousCard();
        });
        buttonPanel.add(previousButton);

        JButton nextButton = new JButton("Next");
        nextButton.addActionListener(e -> {
            if (controller != null)
                controller.nextCard();
        });
        cardComboBox.addActionListener(e -> {
            if (controller != null)
                controller.showCard((String) cardComboBox.getSelectedItem());
        });

        buttonPanel.add(nextButton);
        buttonPanel.add(cardComboBox);

        mainPanel.add(buttonPanel, BorderLayout.PAGE_END);
    }

    @Override
    public void setContoller(CardController controller) {
        this.controller = controller;
    }

    @Override
    public CardLayout getCardLayout() {
        return cardLayout;
    }

    @Override
    public JPanel getCardsPanel() {
        return cardsPanel;
    }

    @Override
    public JPanel getMainPanel() {
        return mainPanel;
    }

    @Override
    public DefaultComboBoxModel<String> getCardComboBoxModel() {
        return cardComboBoxModel;
    }

}

RandomPanel.java

A dummy JPanel that simply displays text that is passed into its constructor with a randomly created background color. It has a nextButton to show that it can interact with the CardLayout that contols its display

class RandomPanel extends JPanel {
    private CardController controller;

    public RandomPanel(String name, CardController controller) {
        this.controller = controller;

        // random color to help distinguish panels
        float hue = (float) Math.random();
        float saturation = (float) (0.5 * Math.random() + 0.5);
        float brightness = (float) (0.2 * Math.random() + 0.8);
        Color randomColor = Color.getHSBColor(hue, saturation, brightness);
        Color borderColor = Color.getHSBColor(hue, saturation, brightness - 0.4f);

        setBorder(BorderFactory.createLineBorder(borderColor, 30));
        setBackground(randomColor);

        JLabel label = new JLabel(name);
        label.setFont(label.getFont().deriveFont(64.0f));
        int gap = 30;
        label.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));

        JPanel buttonPanel = new JPanel();
        buttonPanel.setOpaque(false);
        JButton nextButton = new JButton("Next Card");
        nextButton.addActionListener(e -> {
            if (controller != null)
                controller.nextCard();
        });
        buttonPanel.add(nextButton);

        setLayout(new BorderLayout());
        add(label);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    public CardController getController() {
        return controller;
    }
}

Complete MRE

The complete program in one single copy/paste compile/run file:

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;

import javax.swing.*;

public class MainGUI {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            View view = new CardView();
            CardController controller = new ConcreteController(view);
            String[] cards = { "Card 1", "Card 2", "Card 3", "Card 4", "Card 5",
                    "Card 6", "Card 7", "Card 8", "Card 9", "Card 10" };
            for (String card : cards) {
                JPanel cardPanel = new RandomPanel(card, controller);
                controller.addCard(card, cardPanel);
            }
            controller.showGui();
        });
    }

}

class ConcreteController implements CardController {
    private View view;
    private JFrame frame;

    public ConcreteController(View view) {
        this.view = view;

        view.setContoller(this);
    }

    @Override
    public void showGui() {
        if (frame == null) {
            frame = new JFrame("MainGUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setContentPane(view.getMainPanel());
            frame.pack();
            frame.setLocationRelativeTo(null);
        }
        frame.setVisible(true);
    }

    public View getView() {
        return view;
    }

    @Override
    public void addCard(String name, JPanel card) {
        view.getCardsPanel().add(card, name);
        view.getCardComboBoxModel().addElement(name);
    }

    @Override
    public void showCard(String name) {
        view.getCardLayout().show(view.getCardsPanel(), name);
    }

    @Override
    public void nextCard() {
        view.getCardLayout().next(view.getCardsPanel());
    }

    @Override
    public void previousCard() {
        view.getCardLayout().previous(view.getCardsPanel());
    }
}

interface CardController {
    public void addCard(String name, JPanel card);

    public void showGui();

    public void showCard(String name);

    public void nextCard();

    public void previousCard();
}

interface View {

    void setContoller(CardController controller);

    CardLayout getCardLayout();

    JPanel getCardsPanel();

    JPanel getMainPanel();

    DefaultComboBoxModel<String> getCardComboBoxModel();

}

class CardView implements View {
    private CardController controller;
    private JPanel mainPanel = new JPanel(new BorderLayout());
    private CardLayout cardLayout = new CardLayout();
    private JPanel cardsPanel = new JPanel(cardLayout);
    private DefaultComboBoxModel<String> cardComboBoxModel = new DefaultComboBoxModel<>();
    private JComboBox<String> cardComboBox = new JComboBox<>(cardComboBoxModel);

    public CardView() {
        mainPanel.add(cardsPanel, BorderLayout.CENTER);

        JPanel buttonPanel = new JPanel();

        JButton previousButton = new JButton("Previous");
        previousButton.addActionListener(e -> {
            if (controller != null)
                controller.previousCard();
        });
        buttonPanel.add(previousButton);

        JButton nextButton = new JButton("Next");
        nextButton.addActionListener(e -> {
            if (controller != null)
                controller.nextCard();
        });
        cardComboBox.addActionListener(e -> {
            if (controller != null)
                controller.showCard((String) cardComboBox.getSelectedItem());
        });

        buttonPanel.add(nextButton);
        buttonPanel.add(cardComboBox);

        mainPanel.add(buttonPanel, BorderLayout.PAGE_END);
    }

    @Override
    public void setContoller(CardController controller) {
        this.controller = controller;
    }

    @Override
    public CardLayout getCardLayout() {
        return cardLayout;
    }

    @Override
    public JPanel getCardsPanel() {
        return cardsPanel;
    }

    @Override
    public JPanel getMainPanel() {
        return mainPanel;
    }

    @Override
    public DefaultComboBoxModel<String> getCardComboBoxModel() {
        return cardComboBoxModel;
    }

}

class RandomPanel extends JPanel {
    private CardController controller;

    public RandomPanel(String name, CardController controller) {
        this.controller = controller;

        // random color to help distinguish panels
        float hue = (float) Math.random();
        float saturation = (float) (0.5 * Math.random() + 0.5);
        float brightness = (float) (0.2 * Math.random() + 0.8);
        Color randomColor = Color.getHSBColor(hue, saturation, brightness);
        Color borderColor = Color.getHSBColor(hue, saturation, brightness - 0.4f);

        setBorder(BorderFactory.createLineBorder(borderColor, 30));
        setBackground(randomColor);

        JLabel label = new JLabel(name);
        label.setFont(label.getFont().deriveFont(64.0f));
        int gap = 30;
        label.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));

        JPanel buttonPanel = new JPanel();
        buttonPanel.setOpaque(false);
        JButton nextButton = new JButton("Next Card");
        nextButton.addActionListener(e -> {
            if (controller != null)
                controller.nextCard();
        });
        buttonPanel.add(nextButton);

        setLayout(new BorderLayout());
        add(label);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    public CardController getController() {
        return controller;
    }
}