How to switch JPanels in a JFrame from within the panel? - java

So, I'm trying to make a basic functional menu for a simple game. I tried to do this by creating 2 JPanels, one for the actual game, and another for my menu.
What I'm trying to do is have a button on my Menu panel that when pressed, switches the JPanel being displayed in the parent JFrame from that of the menu to that of the actual game.
Here is my code:
class Menu extends JPanel
{
public Menu()
{
JButton startButton = new JButton("Start!");
startButton.addActionListener(new Listener());
add(startButton);
}
private class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
Container container = getParent();
Container previous = container;
System.out.println(getParent());
while (container != null)
{
previous = container;
container = container.getParent();
}
previous.setContentPane(new GamePanel());
}
}
}
As you can see, I created a Listener for my start button. Inside the listener, I used a while loop to get to the JFrame, via the getParent() method. The program is getting the JFrame object, however it's not letting me call the setContentPane method...
Does anyone know how to get this to work, or a better way to switch back and forth between a menu and game?

Like so :
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CardLayoutDemo extends JFrame {
public final String YELLOW_PAGE = "yellow page";
public final String RED_PAGE = "red page";
private final CardLayout cLayout;
private final JPanel mainPane;
boolean isRedPaneVisible;
public CardLayoutDemo(){
setTitle("Card Layout Demo");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
mainPane = new JPanel();
mainPane.setPreferredSize(new Dimension(250,150));
cLayout = new CardLayout();
mainPane.setLayout(cLayout);
JPanel yellowPane = new JPanel();
yellowPane.setBackground(Color.YELLOW);
JPanel redPane = new JPanel();
redPane.setBackground(Color.RED);
mainPane.add(YELLOW_PAGE, yellowPane);
mainPane.add(RED_PAGE, redPane);
showRedPane();
JButton button = new JButton("Switch Panes");
button.addActionListener(e -> switchPanes() );
setLayout(new BorderLayout());
add(mainPane,BorderLayout.CENTER);
add(button,BorderLayout.SOUTH);
pack();
setVisible(true);
}
void switchPanes() {
if (isRedPaneVisible) {showYelloPane();}
else { showRedPane();}
}
void showRedPane() {
cLayout.show(mainPane, RED_PAGE);
isRedPaneVisible = true;
}
void showYelloPane() {
cLayout.show(mainPane, YELLOW_PAGE);
isRedPaneVisible = false;
}
public static void main(String[] args) {
new CardLayoutDemo();
}
}

Related

Search JPanel by name

I was following the How to use CardLayout Java Swing tutorial and I got to the point where the panel is added to the layout:
JPanel cards;
final static String BUTTONPANEL = "Card with JButtons";
//Create the "cards".
JPanel card1 = new JPanel();
//Create the panel that contains the "cards".
cards = new JPanel(new CardLayout());
cards.add(card1, BUTTONPANEL);
Also they say:
To add a component to a container that a CardLayout object manages,
specify a string that identifies the component being added. For
example, in this demo, the first panel has the string "Card with
JButtons"
So if that string is used to identify the component being added I was wondering if there was a way to get a specific panel in the layout from an AssertJ Swing FrameFixture or directly from the JFrame object, passing the string.
I see that the add method is inherited from java.awt.Container. I was expecting to find a method that would allow me to do something like frame.getComponent(BUTTONPANEL) but that method expects an index as parameter. Did I overlook something?
Also I'm aware of the fact that I could just do card1.setName(BUTTONPANEL) and then in my tests retrieve it with:
window = new FrameFixture(view);
window.panel(BUTTONPANEL);
But what's the point of setting that string in the add method if there is no use for it.
Thanks.
Edit:
I had missed the fact that of course the string BUTTONPANEL it it is used to change the panel from the card layout, like this:
cardLayout.show(getContentPane(), BUTTONPANEL);
And so obviously that string has a use.
However I was looking for a way to be able to get the JPanel object by calling a method on the JFrame or FrameFixture object, passing the string. Something like this:
JPanel buttonPanelPanel = frame.getPanelFromName(BUTTONPANEL);
This is because within the tests I don't have a direct access to the panels, but only to the frame. In order to make assertions on a particular panel I wanted to be able to perform a get of the panel with the string used in add. But maybe this is not possible.
I added a method to the MainPanel class to return a JPanel when you pass a String. I don't use the method, but it's there.
I made all the additional classes inner classes so I could post the code as one block.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CardPanelExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new CardPanelExample());
}
private final MainPanel mainPanel;
public CardPanelExample() {
this.mainPanel = new MainPanel(this);
}
#Override
public void run() {
JFrame frame = new JFrame("CardPanel Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel.getPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public void setNextPanel() {
mainPanel.setNextPanel();
}
public JPanel getPanelFromName(String name) {
return mainPanel.getPanelFromName(name);
}
public class MainPanel {
private final CardLayout cardLayout;
private final JPanel panel;
private JPanel yellowPanel, orangePanel, whitePanel;
private final String[] panelStrings;
private String currentPanelString;
public MainPanel(CardPanelExample view) {
this.cardLayout = new CardLayout();
this.panelStrings = new String[] { "alpha", "beta", "gamma" };
this.currentPanelString = panelStrings[0];
this.panel = createMainPanel(view, cardLayout, panelStrings);
}
private JPanel createMainPanel(CardPanelExample view,
CardLayout cardLayout, String[] panelStrings) {
this.yellowPanel = new ColorPanel(view, Color.YELLOW).getPanel();
this.orangePanel = new ColorPanel(view, Color.ORANGE).getPanel();
this.whitePanel = new ColorPanel(view, Color.WHITE).getPanel();
JPanel panel = new JPanel(cardLayout);
panel.add(yellowPanel, panelStrings[0]);
panel.add(orangePanel, panelStrings[1]);
panel.add(whitePanel, panelStrings[2]);
return panel;
}
public JPanel getPanelFromName(String name) {
if (name.equals(panelStrings[0])) {
return yellowPanel;
} else if (name.equals(panelStrings[1])) {
return orangePanel;
} else if (name.equals(panelStrings[2])) {
return whitePanel;
} else {
return null;
}
}
public void setNextPanel() {
for (int index = 0; index < panelStrings.length; index++) {
if (currentPanelString.equals(panelStrings[index])) {
index = ++index % panelStrings.length;
currentPanelString = panelStrings[index];
cardLayout.show(panel, currentPanelString);
return;
}
}
}
public JPanel getPanel() {
return panel;
}
}
public class ColorPanel {
private final JPanel panel;
public ColorPanel(CardPanelExample view, Color backgroundColor) {
this.panel = createMainPanel(view, backgroundColor);
}
private JPanel createMainPanel(CardPanelExample view, Color backgroundColor) {
JPanel panel = new JPanel(new FlowLayout());
panel.setBackground(backgroundColor);
panel.setBorder(BorderFactory.createEmptyBorder(25, 150, 25, 150));
JButton button = new JButton("Next Panel");
button.addActionListener(new ButtonListener(view));
panel.add(button);
return panel;
}
public JPanel getPanel() {
return panel;
}
}
public class ButtonListener implements ActionListener {
private final CardPanelExample view;
public ButtonListener(CardPanelExample view) {
this.view = view;
}
#Override
public void actionPerformed(ActionEvent event) {
view.setNextPanel();
}
}
}

repaint() method does not paint new frame

I have looked at a lot of answers but i still cannot find a solution. I have a JFrame and two JPanels. I want to remove the one panel and replace it with the second when a button is pressed, but the repaint() method does not refresh the frame. Please help.
Here is my code for the frame:
import javax.swing.*;
import java.awt.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class MainFrame
{
static JFrame mainFrame;
int height = 650;
int width = 1042;
public MainFrame()
{
mainFrame = new JFrame();
mainFrame.setBounds(0, 0, width, height);
mainFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainFrame.setResizable(false);
}
public static void main(String[] args)
{
new MainMenu();
mainFrame.setVisible(true);
}
}
This is the code for my MainMenu panel
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import static java.awt.Color.CYAN;
import static java.awt.Color.red;
public class MainMenu extends MainFrame
{
public MainMenu()
{
components();
}
//variable decleration
JPanel menuPanel;
JLabel title;
JButton periodicTable;
private void components()
{
int buttonW = 500;
int buttonH = 50;
//creating panel
menuPanel = new JPanel();
menuPanel.setLayout(null);
menuPanel.setBackground(CYAN);
//creating title label
title = new JLabel("Application Title", SwingConstants.CENTER);
title.setFont(new Font("Calibri Body", 0, 50));
title.setBounds(width / 3 - buttonW / 2, 50, buttonW, buttonH + 10);
//creating periodic table button
periodicTable = new JButton();
periodicTable.setText("Periodic Table");
periodicTable.setBounds(width / 3 - buttonW / 2, 50 + buttonH + 60, buttonW, buttonH);
periodicTable.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
periodicTableActionPerformed(event);
}
});
//adding components to panel
menuPanel.add(title);
menuPanel.add(periodicTable);
//adding panel to MainFrame
mainFrame.add(menuPanel);
}
private void periodicTableActionPerformed(ActionEvent event)
{
mainFrame.remove(menuPanel);
mainFrame.repaint();
new PeriodicTable();
mainFrame.repaint();
}
}
And finally my PeriodicTable panel
import javax.swing.*;
import java.awt.*;
public class PeriodicTable extends MainFrame
{
public PeriodicTable()
{
periodicComponents();
}
JPanel ptPanel;
private void periodicComponents()
{
ptPanel = new JPanel();
ptPanel.setLayout(null);
ptPanel.setBackground(Color.RED);
mainFrame.add(ptPanel);
}
}
I have no idea why you are extending MainFrame. Looks unnecessary to me.
I want to remove the one panel and replace it with the second when a button is pressed
Then use a CardLayout. Read the section from the Swing tutorial on How to Use CardLayout for a working example.
The tutorial will show you how to better structure your code.
Your PeriodicTable extends MainFrame. When creating new PeriodicTable you create with it new MainFrame which has its own instance of JFrame (MainFrame.mainFrame). You need to add that panel to existing mainFrame in MainMenu
I suggest removing changing your PeriodicTable class like this:
import javax.swing.*;
import java.awt.*;
public class PeriodicTable extends JPanel // Not MainFrame, but new custom panel
{
public PeriodicTable()
{
periodicComponents();
}
private void periodicComponents()
{
// You don't need ptPanel anymore, because `this` is JPanel
setLayout(null);
setBackground(Color.RED);
}
}
and change your actionPerformed function to something like this:
private void periodicTableActionPerformed(ActionEvent event)
{
mainFrame.remove(menuPanel); // Remove old panel
mainFrame.add(new PeriodicTable()); // Create and add to existing mainFrame
mainFrame.repaint(); // Just one repaint at the end
// I think it will work even without repaint, because add and remove should schedule repainting as well
}

BorderLayout orientation

I'm trying to create the top buttons of a window. I have a JFrame and a JPanel with the differents buttons when I try to add the panel with the buttons to a JPanel on the frame, it doesn't show... digging and trying to find the solution, I realize that the issue is when I set the orientation to the panel with the buttons on the BorderLayout panel. I think that it might be something dumb that I haven't realize but I haven't found any issue like this.
The issue is here when I set the orientation:
contentPanel.add(buttons,BorderLayout.PAGE_START);
if I remove the:
BorderLayout.PAGE_START
it works
This is my Frame:
package view;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.CardLayout;
import java.awt.BorderLayout;
public class MainFrame extends JFrame{
private JPanel contentPanel, layOutPanel;
private CardLayout mainCardLayout;
private BorderLayout borderLayout;
private static MainFrame instance = null;
private FrameButtonsPanel buttons;
private MainFrame(){
setSize(1000,700);
//setUndecorated(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPanel = new JPanel();
borderLayout = new BorderLayout();
contentPanel.setLayout(borderLayout);
add(contentPanel);
buttons = new FrameButtonsPanel();
buttons.setBackground(Color.red);
contentPanel.add(buttons,BorderLayout.PAGE_START);
/*layOutPanel = new JPanel();
mainCardLayout = new CardLayout();
layOutPanel.setLayout(mainCardLayout);
layOutPanel.setBackground(Color.red);
contentPanel.add(layOutPanel,BorderLayout.SOUTH);*/
}
public static MainFrame getInstance(){
if (instance == null){
instance = new MainFrame();
}
return instance;
}
public static void main(String[] args) {
MainFrame.getInstance().setVisible(true);
}
}
and this is my panel with the buttons:
package view;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.Spring;
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class FrameButtonsPanel extends JPanel{
private Spring spring;
private JButton iconify, maximize, close;
public FrameButtonsPanel(){
SpringLayout mySpring = new SpringLayout();
setLayout(mySpring);
iconify = new JButton("-");
add(iconify);
maximize = new JButton("O");
add(maximize);
close = new JButton("X");
add(close);
spring = Spring.constant(850,850,2000);
mySpring.putConstraint(SpringLayout.WEST,iconify,spring,SpringLayout.WEST,this);
mySpring.putConstraint(SpringLayout.WEST,maximize,3,SpringLayout.EAST,iconify);
mySpring.putConstraint(SpringLayout.WEST,close,3,SpringLayout.EAST,maximize);
mySpring.putConstraint(SpringLayout.EAST,this,3,SpringLayout.EAST,close);
iconifyWindow();
maximizeWindow();
closeWindow();
}
private void iconifyWindow(){
iconify.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
MainFrame.getInstance().setExtendedState(JFrame.ICONIFIED);
}
});
}
private void maximizeWindow(){
maximize.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
if(MainFrame.getInstance().getExtendedState() == JFrame.MAXIMIZED_BOTH){
MainFrame.getInstance().setExtendedState(JFrame.NORMAL);
}else{
MainFrame.getInstance().setExtendedState(JFrame.MAXIMIZED_BOTH);
}
}
});
}
private void closeWindow(){
close.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
}
}
I have no idea why you are trying to use a SpringLayout to display buttons.
Just use a JPanel with a right aligned FlowLayout.
Read the FlowLayout API for more information on how to right align the components added to the panel.

When i change JPanel in JFrame i lose focus

As in subject: When i change JPanel in JFrame i lose focus. I have class Game, Action, Button.
I have also class Stage when drawing the game stage.
At first in Game i have Action panel, which contains buttons, after push button NewGame i change panel in Game to Stage, but i cant control ship, which i am flying.
How to fix it or how to do it different?
Action.java
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.io.FileNotFoundException;
import javax.swing.*;
#SuppressWarnings("serial")
public class Action extends JPanel {
public static final int xxx = 800;
public static final int yyy = 600;
private Button buttonPanel;
public Action(Game game) {
setLayout(new FlowLayout());
setPreferredSize(new Dimension(xxx, yyy));
buttonPanel = new Button(game);
add(buttonPanel);
setVisible(true);
}
}
Button.java
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Button extends JPanel implements ActionListener{
public static final int xxx = 100;
public static final int yyy = 300;
private JButton NewGame;
private JButton Scores;
private JButton Exit;
private Game game;
public Button(Game game) {
NewGame = new JButton("NewGame");
Scores = new JButton("Scores");
Exit = new JButton("Wyjście");
this.game=game;
NewGame.addActionListener(this);
setLayout(new FlowLayout());
setPreferredSize(new Dimension(xxx, yyy));
add(NewGame);
add(Scores);
add(Exit);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if(source == NewGame){
game.setPanel("stage");
}
;
}
}
Game.java
#SuppressWarnings("serial")
public class Game extends JFrame {
private Action menu;
private static Stage stage = new Stage();
public Game() {
super("Lunar Lander");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(Stage.WIDTH,Stage.HEIGHT);
setMinimumSize(new Dimension(400,300));
this.setResizable(true);
//stage = new Stage(0);
menu = new Action(this);
add(menu);
pack();
//setPanel("stage");
setVisible(true);
}
public void setPanel(String panel) {
if (panel=="stage") {
remove(menu);
add(stage);
pack();
} else if (panel=="menu") {
remove(stage);
add(menu);
pack();
}
}
public static void main(String[] args) throws FileNotFoundException {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Game();
}
});
Thread a = new Thread(stage);
a.start();
while (a.isAlive()) {}
Scores scores = new Scores();
scores.changeScores(stage.getPlayer().getName(),stage.getPlayer().getPoints());
}
}
but i cant control ship, which i am flying.
KeyEvents are only passed to the component with focus. When you swap panels the panel no longer has focus.
Don't use a KeyListener. Instead you should be using Key Bindings. Then you can handle the event even if the component doesn't have focus.
See Motion Using the Keyboard for more information and working examples.
Edit:
Don't use "==" for String comparisons. Use the String.equals(...) method.
Variable names should NOT start with an upper case character.

How would I use a button in Java to open Jframe over existing Jframe

I have 2 frames, the first frame has the nothing more and a button, which leads to another frame which will have all the components, like tabs which have more components.
The code I am using is:
button_1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
JFrame Frame_2 = new JFrame();
Frame_1.setVisible(false);
Frame_2.setVisible(true);
}
});
this is creating a new separate frame , but i want to create new JFrame over existing JFrame
update
#VinceEmigh +1
Thanks for the detail custom solution. It shows that someone is really willing to help, I am a self learner , started just 3 months ago so your code is bit difficult to understand, but the idea of using cardlayout did the work and i came up with a solution.
JFrame guiFrame = new JFrame();
CardLayout cards;
JPanel cardPane;
JButton B_1 = new JButton("Next Card");
B_1.setActionCommand("Next Card");
B_1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent event)
{
cards.next(cardPane);
}
});
cards = new CardLayout();
cardPane = new JPanel();
cardPane.setLayout(cards);
cards.show(cardPane, "Main");
JPanel Card_1 = new JPanel();
JLabel background_1 = new JLabel(new ImageIcon("C:\\Users\\ME\\Desktop\\Back1.jpg"));
Card_1.add(background_1);
Card_1.add(B_1);
JPanel Card_2 = new JPanel();
JLabel background_2 = new JLabel(new ImageIcon("C:\\Users\\ME\\Desktop\\Back2.jpg"));
Card_2.add(background_2);
cardPane.add(Card_1, "Main");
cardPane.add(Card_2, "Sub");
You shouldnt use 2 frames. You should use 1 frame, then switch between panels in the frame using CardLayout. Unless you're referring to nesting a frame within a frame, creating 2 different frames for 1 applicarion is typically bad practice, and should be avoided if possible.
Set your frames layout to CardLayout, add 2 panels to your frame. One panel contains the button, the other has the components.
When your button event triggers throuhh an actionlistener, switch out the panels using the cardlayout you put for the frames layout.
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class App extends JFrame {
private CardLayout cl = new CardLayout();
private JPanel firstPanel = new FirstPanel();
private JPanel secondPanel = new SecondPanel();
public App() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setLayout(cl);
add(firstPanel, "first");
add(secondPanel, "second");
setLocationRelativeTo(null);
setVisible(true);
}
public void switchPanel(String name) {
cl.show(getContentPane(), name);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
App app = new App();
}
});
}
class FirstPanel extends JPanel implements ActionListener {
private JButton button = new JButton("Button");
public FirstPanel() {
button.addActionListener(this);
add(button);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == button) {
switchPanel("second");
}
}
}
class SecondPanel extends JPanel { }
}

Categories