From a variety of resources (this website, a book, a friend) I have managed to make a JFrame(JFrame1) that responds to a button. It makes another JFrame (JFrame2), and changes the JFrame.setVisible() to false.
What I'm trying to do is make it so that when a declared Back button is pressed, it closes JFrame2 and sets JFrame1 visibility to true. That's all fine, but when I do JFrame2.setVisiblity(false), JFrame2 is still visible. I've tried dispose(); but that doesn't work either.
I'm also wondering, since I've read on stackoverflow, that creating multiple JFrames is bad programming. So should I use JDialogs instead?
I am trying to display a bunch of information, and allow you to interact with the GUI to navigate around the information. The information would be arranged by alphabetical order.
Also, I'm not sure how to post code on here, so if you need to see what I currently have, just tell me how to post code :D
You are better of using a single frame, using something like JPanels to hold you UI components. This way you can simply switch it the panels as you need, possibly with something like CardLyout
By moving your UI to panels, you are also decoupling your code, giving your more flexibility and reuse potential.
Updated with basic example
This basically uses a simple model to change out different views. You could use a different style of model that listens to changes to the views and then makes choices on there behalf (which would generally be my preferred method), it depends on what you want to do...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CardLayoutDemo {
public static void main(String[] args) {
new CardLayoutDemo();
}
public CardLayoutDemo() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new MasterPane());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GameModel {
private JPanel view;
private JPanel lastView;
private JPanel currentView;
private WelcomePane welcomePane;
private GamePane gamePane;
private SettingsPane settingsPane;
public GameModel(JPanel view) {
this.view = view;
welcomePane = new WelcomePane(this);
gamePane = new GamePane(this);
settingsPane = new SettingsPane(this);
}
public void welcome() {
lastView = currentView;
view.removeAll();
view.add(welcomePane);
view.revalidate();
view.repaint();
currentView = welcomePane;
}
public void newGame() {
lastView = currentView;
view.removeAll();
view.add(gamePane);
view.revalidate();
view.repaint();
currentView = gamePane;
}
public void settings() {
lastView = currentView;
view.removeAll();
view.add(settingsPane);
view.revalidate();
view.repaint();
currentView = settingsPane;
}
public void back() {
if (lastView != null) {
view.removeAll();
view.add(lastView);
view.revalidate();
view.repaint();
currentView = lastView;
lastView = null;
}
}
}
public class MasterPane extends JPanel {
public MasterPane() {
setLayout(new BorderLayout());
GameModel model = new GameModel(this);
model.welcome();
}
}
public class WelcomePane extends JPanel {
private GameModel model;
public WelcomePane(GameModel gameModel) {
this.model = gameModel;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JButton btnStart = new JButton("New Game");
JButton btnSettings = new JButton("Settings");
add(new JLabel("Welcome"), gbc);
add(btnStart, gbc);
add(btnSettings, gbc);
btnSettings.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.settings();
}
});
btnStart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.newGame();
}
});
}
}
public class SettingsPane extends JPanel {
private GameModel model;
public SettingsPane(GameModel gameModel) {
this.model = gameModel;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("Go ahead, make some changes..."), gbc);
JButton back = new JButton("Back");
back.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.back();
}
});
add(back, gbc);
}
}
public class GamePane extends JPanel {
private GameModel model;
public GamePane(GameModel model) {
this.model = model;
setLayout(new GridBagLayout());
add(new JLabel("All your base are belong to us"));
}
}
}
Related
So I'm using cardLayout in one of my programs and I'm trying to make it so that when you click a button the next panel loads. I have a panelHolder class where the cardlayout is held and every time the button on the panel is pressed, it would call a method in the panelHolder class that depending on the button sets a certain boolean variable to true and calls repaint (where the panels are shown). For some reason my button isn't working and I can't seem to find out why. Can someone help me?
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.Arrays;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.*;
public class SheetReader101 extends JFrame {
public SheetReader101(){
super("SheetReader101");
setSize(2000,1000);
setLocation(0,0);
setResizable(true);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
PanelHolder pg2 = new PanelHolder();
setContentPane(pg2);
setVisible(true);
}
public static void main(String[]args){
SheetReader101 z1 = new SheetReader101();
}
}
class PanelHolder extends JPanel { // HERE
CardLayout clayout = new CardLayout();
PianoGameContent x;
tutorial y;
boolean [] paneldecide;
PanelHolder() {
super();
y = new tutorial();
x = new PianoGameContent();
setLayout(clayout);
this.add("Tutorial", y);
this.add("FreePlay Mode", x);
paneldecide = new boolean[15];
}
public static void main(String[]args){
PanelHolder z1 = new PanelHolder();
z1.run();
}
public void run(){
layoutShower(0);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
}
public void layoutShower (int decide){
{
PianoGameContent y2 = new PianoGameContent();
PanelHolder.this.add("Piano", y2);
System.out.println("intro slide run");
if(decide == 1){
PanelHolder.this.add("Piano", y2);
System.out.println("testing11");
clayout.show(PanelHolder.this,"Piano");
}
}
}
}
I "suspect" that the core problem has to do with the original code you posted, where you're making a new instance of PanelHolder in your child view's ActionListener and then are attempting to switch views, this new instance has no relationship to the instance that is on the screen.
There are a few ways you can manage CardLayout, my preferred way is to use some kind of "navigation" controller which defines how navigation works, for example, you could have "next" and "previous" or "back", or you could define the actual views that can be displayed, ie showMenuView, showTutorialView etc, depending on how much control you want to give your sub views.
The following is a simple example which demonstrates the basic idea, it uses a enum to define the available views (as it has more meaning than 0, 1... and I don't need to remember the actual names of the views, the IDE can provide auto correct for that ;))
I create and add each view up front when I create the PanelHolder, I also pass each view an instance of the NavigationController, so they can interact with it
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class JavaApplication1013 {
public static void main(String[] args) {
new JavaApplication1013();
}
public JavaApplication1013() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PanelHolder());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum View {
MENU,
TUTORIAL,
FREEPLAY;
}
public interface NavigationController {
public void showView(View view);
}
public class PanelHolder extends JPanel implements NavigationController {
private CardLayout cardLayout;
public PanelHolder() {
cardLayout = new CardLayout();
setLayout(cardLayout);
add(new MenuView(this), View.MENU.name());
add(new TutorialView(this), View.TUTORIAL.name());
add(new FreePlayView(this), View.FREEPLAY.name());
}
#Override
public void showView(View view) {
cardLayout.show(this, view.name());
}
}
public abstract class ViewPane extends JPanel {
private NavigationController controller;
public ViewPane(NavigationController controller) {
this.controller = controller;
}
public NavigationController getController() {
return controller;
}
protected void showView(View view) {
controller.showView(view);
}
}
public class MenuView extends ViewPane {
public MenuView(NavigationController controller) {
super(controller);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
JButton tut = new JButton("Tutorial");
JButton freePlay = new JButton("Free Play");
add(tut, gbc);
add(freePlay, gbc);
tut.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showView(View.TUTORIAL);
}
});
freePlay.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showView(View.FREEPLAY);
}
});
}
}
public class TutorialView extends ViewPane {
public TutorialView(NavigationController controller) {
super(controller);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JButton menu = new JButton("Menu");
add(new JLabel("Tutorial"), gbc);
add(menu, gbc);
menu.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showView(View.MENU);
}
});
}
}
public class FreePlayView extends ViewPane {
public FreePlayView(NavigationController controller) {
super(controller);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JButton menu = new JButton("Menu");
add(new JLabel("Free Play"), gbc);
add(menu, gbc);
menu.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showView(View.MENU);
}
});
}
}
}
Take a closer look at How to Use CardLayout for more details
Im making an application that lets me perform hashes, at the top I want to have a group of check boxes basically saying the four different hash types I have. When the check boxes are selected I want the labels to appear showing the hashed entered text.
I've attached an image to hopefully make it easier to understand what I mean. The reason I'm doing this is so that when the final program is made with almost 10 rows of text boxes and labels it can be reduced only to show the ones that the user wishes to see.This hopefully should explain what I mean.
I've been able to get it so the checkboxes make it visible or not visible but that also then just leaves a blank space where one row of labels used to be rather than moving everything up a row
I've now added my coding so people can see how I'm doing it currently and help define where needs to be modified
import java.awt.*;
import java.awt.event.*;
import java.security.*;
import javax.swing.*;
public class Hasher extends JFrame implements ActionListener {
String UserInput;
private JTextField textInputField;
private static JLabel MD5Hashed,MD5Label;
private static JCheckBox MD5Check, SHA1Check, SHA256Check, FileCheck;
private JFrame contentPane;
public Hasher() {
this.setTitle("Hasher");
Container contentPane = this.getContentPane();
contentPane.setLayout(new GridLayout(0,1) );
contentPane.setBackground(new Color(88,148,202));
//CheckBoxes
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
JPanel checkBoxPanel = new JPanel();
MD5Check = new JCheckBox("MD5");
MD5Check.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
boolean Visible = MD5Check.isSelected();
MD5Hashed.setVisible(Visible);
MD5Label.setVisible(Visible);
}
});
checkBoxPanel.add(MD5Check);
SHA1Check = new JCheckBox("SHA-1");
checkBoxPanel.add(SHA1Check);
SHA256Check = new JCheckBox("SHA-256");
checkBoxPanel.add(SHA256Check);
FileCheck = new JCheckBox("File Hashing");
checkBoxPanel.add(FileCheck);
mainPanel.add(checkBoxPanel);
contentPane.add(mainPanel);
//Entered data to perform hash on
contentPane.add(new JLabel (" Enter text to hash"));
textInputField = new JTextField();
//HashingProcess inputListener = new HashingProcess( );
//textInputField.addActionListener(inputListener);
contentPane.add( textInputField);
//MD5 hash is completed
MD5Label = new JLabel( " Using MD5 the hash is: " );
contentPane.add( MD5Label);
MD5Hashed = new JLabel( "??") ;
contentPane.add( MD5Hashed );
MD5Hashed.setVisible(false);
MD5Label.setVisible(false);
}
public static void main(String[] args) {
Hasher theWindow = new Hasher( );
theWindow.setSize(400, 400 );
theWindow.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
theWindow.setVisible(true);
}
}
You have two frames...
public class Hasher extends JFrame implements ActionListener {
//...
private JFrame contentPane;
//..
This immediately raises the question, which one is actually on the screen and which one are you actually interacting with? While this might not be directly related to your problem, it is confusing.
As a general rule of thumb, don't extend from top level containers like JFrame, it locks you into a single use case and can cause no end of confusion. Instead, start with a JPanel and add it to an instance of JFrame or any other container you like.
The "main probable" problem is, Swing is lazy. It won't update the UI until you tell it or the UI is resized.
When adding and removing components, you need to call revalidate and repaint on the container to trigger an update - this is what's actually going wrong in your code, you're referencing contentPane, by that is private JFrame contentPane; and not the contentPane of the JFrame from which Hasher extends ... see, confusion.
Happening dug around your code a bit, it's obvious that you have a lot of repeated operations going on, the only core difference is the algorithm used to hash the text.
So, with that in mind, we can create some basic classes to do most of the work, for example...
public class HashPane extends JPanel {
private JLabel hashedLabel;
private JLabel promptLabel;
private HashAlgorithim algorithim;
public HashPane(String labelText, HashAlgorithim algorithim) {
setOpaque(false);
this.algorithim = algorithim;
setLayout(new FlowLayout(FlowLayout.LEFT));
promptLabel = new JLabel(labelText);
add(promptLabel);
hashedLabel = new JLabel("??");
add(hashedLabel);
}
public void setText(String text) {
hashedLabel.setText(algorithim.generateHash(text));
}
}
This is just two labels, which show a prompt and a result. The result is generated via a plug algorithm which is used to generate the hash for the supplied text
The algorithm itself is just a interface which defines the basic contract...
public interface HashAlgorithm {
public String generateHash(String from);
}
You would then need to create an implementation of each algorithm you wanted to use
Now, making a panel visible/invisible simply becomes a matter of associating a JCheckBox with a HashPane which can be achieved through a simple Map...
public class Hasher extends JPanel {
//...
private Map<JCheckBox, HashPane> mapPanes;
public Hasher() {
//...
HashPane md5Pane = new HashPane("MD5 hash = ", new NotAMD5Alorithim());
//...
HashPane sha1Pane = new HashPane("SHA-1 hash = ", new NotAMSHA1Alorithim());
//..
mapPanes = new HashMap<>(25);
mapPanes.put(MD5Check, md5Pane);
mapPanes.put(SHA1Check, sha1Pane);
//...
You can then use a single ActionListener to manage all the JCheckBoxs
public class Hasher extends JPanel {
//...
private Map<JCheckBox, HashPane> mapPanes;
public Hasher() {
//...
ActionHandler listener = new ActionHandler();
for (Entry<JCheckBox, HashPane> entry : mapPanes.entrySet()) {
entry.getKey().addActionListener(listener);
entry.getValue().setVisible(false);
}
}
protected class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JCheckBox cb = (JCheckBox) e.getSource();
HashPane hashPane = mapPanes.get(cb);
hashPane.setVisible(cb.isSelected());
revalidate();
repaint();
}
}
And because I pretty much butchered your code...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Hasher extends JPanel {
String UserInput;
private JTextField textInputField;
private static JCheckBox MD5Check, SHA1Check, SHA256Check, FileCheck;
private Map<JCheckBox, HashPane> mapPanes;
public Hasher() {
setLayout(new BorderLayout());
setBackground(new Color(88, 148, 202));
//CheckBoxes
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
JPanel checkBoxPanel = new JPanel();
MD5Check = new JCheckBox("MD5");
checkBoxPanel.add(MD5Check);
SHA1Check = new JCheckBox("SHA-1");
checkBoxPanel.add(SHA1Check);
SHA256Check = new JCheckBox("SHA-256");
checkBoxPanel.add(SHA256Check);
FileCheck = new JCheckBox("File Hashing");
checkBoxPanel.add(FileCheck);
mainPanel.add(checkBoxPanel);
add(mainPanel, BorderLayout.NORTH);
JPanel centerPane = new JPanel(new BorderLayout());
centerPane.setOpaque(false);
JPanel inputPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
inputPane.setOpaque(false);
//Entered data to perform hash on
inputPane.add(new JLabel("Enter text to hash: "));
textInputField = new JTextField(20);
inputPane.add(textInputField);
centerPane.add(inputPane, BorderLayout.NORTH);
JPanel output = new JPanel(new GridBagLayout());
output.setOpaque(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
gbc.gridwidth = GridBagConstraints.REMAINDER;
HashPane md5Pane = new HashPane("MD5 hash = ", new NotAMD5Alorithim());
output.add(md5Pane, gbc);
gbc.gridx = 0;
gbc.gridy++;
HashPane sha1Pane = new HashPane("SHA-1 hash = ", new NotAMSHA1Alorithim());
output.add(sha1Pane, gbc);
// last pane
gbc.gridy++;
gbc.weighty = 1;
output.add(new JLabel(), gbc);
centerPane.add(output);
add(centerPane);
mapPanes = new HashMap<>(25);
mapPanes.put(MD5Check, md5Pane);
mapPanes.put(SHA1Check, sha1Pane);
//...
ActionHandler listener = new ActionHandler();
for (Entry<JCheckBox, HashPane> entry : mapPanes.entrySet()) {
entry.getKey().addActionListener(listener);
entry.getValue().setVisible(false);
}
}
protected class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JCheckBox cb = (JCheckBox) e.getSource();
HashPane hashPane = mapPanes.get(cb);
hashPane.setVisible(cb.isSelected());
revalidate();
repaint();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Hasher());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface HashAlgorithm {
public String generateHash(String from);
}
public class NotAMD5Alorithim implements HashAlgorithm {
#Override
public String generateHash(String from) {
return "bananas";
}
}
public class NotAMSHA1Alorithim implements HashAlgorithm {
#Override
public String generateHash(String from) {
return "bananas";
}
}
public class HashPane extends JPanel {
private JLabel hashedLabel;
private JLabel promptLabel;
private HashAlgorithm algorithim;
public HashPane(String labelText, HashAlgorithm algorithim) {
setOpaque(false);
this.algorithim = algorithim;
setLayout(new FlowLayout(FlowLayout.LEFT));
promptLabel = new JLabel(labelText);
add(promptLabel);
hashedLabel = new JLabel("??");
add(hashedLabel);
}
public void setText(String text) {
hashedLabel.setText(algorithim.generateHash(text));
}
}
}
Having said all that, I might be tempted to have the JCheckBox in the HashPane and has the HashPane always visible and simply disable the output text ... or simply not bother and always have all the algorithms available all the time
you need to handle Events for all check-box while check/uncheck,
public void actionPerformed(ActionEvent actionEvent) {
if(all check-box un-checked){
// someLable.setVisible(false);
}else if(MD5 checked){
// peform operation based upon MD5
// someLable.setVisible(true);
}
// likewise for all others
}
Change
public void actionPerformed(ActionEvent e) {
boolean Visible = MD5Check.isSelected();
MD5Hashed.setVisible(Visible);
MD5Label.setVisible(Visible);
}
to:
public void actionPerformed(ActionEvent e) {
boolean Visible = MD5Check.isSelected();
MD5Hashed.setVisible(Visible);
MD5Label.setVisible(Visible);
contentPane.validate();
contentPane.repaint();
}
If You want to remove MD5Hashed and MD5Label then something like this:
{
if(MD5Check.isSelected()){
MD5Hashed.remove();
MD5Label.remove();
}
else{
contentPane.add( MD5Label);
contentPane.add( MD5Hashed );
}
contentPane.validate();
contentPane.repaint();
}
Hey Guys my problem is when I place the mouse on a JButton in my JFrame, I want it to show a list of JButtons on its left.
I don't known how to do that really I feel like I'm blocked and I cant make any progress in my project.
I'd would be grateful if you could help me and thanks in advance.
Can you create the list of buttons in a JPanel, add it to your JFrame and then call myPanel.setVisible(false). When you click your button then call myPanel.setVisible(true)?
As for ensuring that myPanel is positioned correctly you will want to use a Layout Manager
Or is there a more complex behaviour you want?
A basic option would be to use a MouseListener and a CardLayout. The MouseListener would be used to determine when the mouse cursor enters/exists a given component and the CardLayout would be used to display the appropriate sub component for each "menu" element.
I have to say, JButton would be me last choice for the "menu" item, in most cases, a JLabel would be preferred or even perhaps using a JMenu, which can can have sub menus, which can be displayed automatically might be a better choice, or even a JComboBox....
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ShowStuff {
public static void main(String[] args) {
new ShowStuff();
}
public ShowStuff() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
MenuPane menu = new MenuPane();
menu.addMenu("Fruit", new FruitPane());
menu.addMenu("Meat", new MeatPane());
menu.addMenu("Dairy", new DairyPane());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(menu);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MenuPane extends JPanel {
private JPanel subMenu;
private JPanel menu;
private CardLayout cardLayout;
private MouseListener mouseHandler;
public MenuPane() {
setLayout(new BorderLayout());
cardLayout = new CardLayout();
subMenu = new JPanel(cardLayout);
menu = new JPanel(new GridBagLayout());
add(subMenu);
add(menu, BorderLayout.WEST);
subMenu.add(new JPanel(), "BLANK");
mouseHandler = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (e.getSource() instanceof JButton) {
JButton btn = (JButton) e.getSource();
cardLayout.show(subMenu, btn.getText());
}
}
#Override
public void mouseExited(MouseEvent e) {
cardLayout.show(subMenu, "BLANK");
}
};
}
public void addMenu(String name, JPanel subMenuPane) {
JButton button = new JButton(name);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
menu.add(button, gbc);
subMenu.add(subMenuPane, name);
button.addMouseListener(mouseHandler);
}
}
public abstract class ButtonPane extends JPanel {
private int gridy = 0;
public ButtonPane() {
setLayout(new GridBagLayout());
}
protected void addButton(String name) {
JButton btn = new JButton(name);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = gridy++;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(btn, gbc);
}
}
public class FruitPane extends ButtonPane {
public FruitPane() {
addButton("Banana");
addButton("Grapes");
addButton("Apples");
addButton("Tomatoes");
}
}
public class MeatPane extends ButtonPane {
public MeatPane() {
addButton("Lamb");
addButton("Beef");
addButton("Pork");
addButton("Mince");
}
}
public class DairyPane extends ButtonPane {
public DairyPane() {
addButton("Milk");
addButton("Cream");
addButton("Cheese");
addButton("Yoghurt");
}
}
}
public class MainWindow extends JPanel {
public static MainWindow instance = new MainWindow();
private CardLayout cards = new CardLayout();
public MainWindow() {
setLayout(cards);
add(new FirstPage(), Pages.FIRST.toString());
add(new SecondPage(), Pages.SECOND.toString());
add(new ThirdPage(), Pages.THIRD.toString());
}
public void showPage(Pages page) {
cards.show(this, page.toString());
}
}
the showPage(page); method works fine if I call it in the constructor of MainWindow. But when I try to call MainWindow.instance.showPage(Pages.SECOND); from an ActionListener in FirstPage nothing happens. I've checked that the showPage(page) method works correctly. I've checked that the ActionEvent is fired and enters the correct if/else clause. What am I doing wrong, why isn't my second page showing?
public class FirstPage extends JPanel {
private JButton showSecond = new JButton("Show Second");
private JButton showThird = new JButton("Show Third");
public FirstPage() {
insertButton(showSecond);
insertButton(showThird);
}
private void insertButton(JButton button) {
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == showSecond) {
MainWindow.instance.showPage(Pages.SECOND);
} else {
MainWindow.instance.showPage(Pages.THIRD);
}
}
});
this.add(button);
}
}
It would suggest a reference issue. public static MainWindow instance = new MainWindow(); looks suspicious, as you would have had to create an instance of MainWindow first for it to be initialise which suggests you now have two instances of MainWindow, one on the screen and one that is not
Using static in this way is a bad idea, as it leads to issues like this. Instead you should pass a reference of the controller to the page. The controller would define the actions that each page could perform (and if done right, would be defined as an interface)
Alternatively, you could separate the navigation from the pages into a separate mechanism, this means the pages don't care and can simply displayed in any order you want or reused else where
Example #1 - Controller based pages
This examples defines a simple controller which the pages can call in order to effect the navigation of the pages
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CardLayoutExample {
public static void main(String[] args) {
new CardLayoutExample();
}
public CardLayoutExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Wizard());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface NavigationController {
public void nextPage();
public void previousPage();
public void lastPage();
public void firstPage();
}
public interface Page {
public NavigationController getNavigationController();
public JComponent getView();
public String getName();
}
public class Wizard extends JPanel implements NavigationController {
private List<Page> pages;
private Page currentPage;
private CardLayout cardLayout;
public Wizard() {
cardLayout = new CardLayout();
pages = new ArrayList<>(25);
setLayout(cardLayout);
pages.add(new FirstPage("Page01", this));
pages.add(new SecondPage("Page02", this));
pages.add(new ThirdPage("Page03", this));
for (Page page : pages) {
add(page.getView(), page.getName());
}
firstPage();
}
#Override
public void nextPage() {
int index = pages.indexOf(currentPage);
index++;
if (index < pages.size()) {
cardLayout.next(this);
currentPage = pages.get(index);
}
}
#Override
public void previousPage() {
int index = pages.indexOf(currentPage);
index--;
if (index >= 0) {
cardLayout.previous(this);
currentPage = pages.get(index);
}
}
#Override
public void lastPage() {
Page page = pages.get(pages.size() - 1);
showPage(page);
}
#Override
public void firstPage() {
Page page = pages.get(0);
showPage(page);
}
protected void showPage(Page page) {
cardLayout.show(this, page.getName());
currentPage = page;
}
}
public abstract class AbstractPage extends JPanel implements Page, ActionListener {
private NavigationController navigationController;
private JPanel buttons;
private String name;
public AbstractPage(String name, NavigationController navigationController) {
this.name = name;
this.navigationController = navigationController;
setLayout(new BorderLayout());
buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));
add(buttons, BorderLayout.SOUTH);
}
protected void insertButton(JButton button) {
button.addActionListener(this);
buttons.add(button);
}
#Override
public NavigationController getNavigationController() {
return navigationController;
}
#Override
public JComponent getView() {
return this;
}
#Override
public String getName() {
return super.getName();
}
}
public class FirstPage extends AbstractPage implements Page {
private JButton next = new JButton("Next >");
public FirstPage(String name, NavigationController controller) {
super(name, controller);
JLabel label = new JLabel("First page");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
insertButton(next);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == next) {
getNavigationController().nextPage();
}
}
}
public class SecondPage extends AbstractPage implements Page {
private JButton next = new JButton("Next >");
private JButton previous = new JButton("< Previous");
public SecondPage(String name, NavigationController controller) {
super(name, controller);
JLabel label = new JLabel("Second page");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
insertButton(previous);
insertButton(next);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == next) {
getNavigationController().nextPage();
} else if (e.getSource() == previous) {
getNavigationController().previousPage();
}
}
}
public class ThirdPage extends AbstractPage implements Page {
private JButton previous = new JButton("< Previous");
public ThirdPage(String name, NavigationController controller) {
super(name, controller);
JLabel label = new JLabel("Third page");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
insertButton(previous);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == previous) {
getNavigationController().previousPage();
}
}
}
}
Example #2 - central controller example
This example separates the controller from the pages, so that the buttons are not part of the pages themselves. This frees up the pages/views to be anything you need them to be
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class CardLayoutExample2 {
public static void main(String[] args) {
new CardLayoutExample2();
}
public CardLayoutExample2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new WizardPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class WizardPane extends JPanel {
private List<String> pages;
private String currentPage;
private JButton first;
private JButton previous;
private JButton next;
private JButton last;
private CardLayout cardLayout;
private JPanel contentPane;
public WizardPane() {
setLayout(new BorderLayout());
cardLayout = new CardLayout();
pages = new ArrayList<>(3);
contentPane = new JPanel(cardLayout);
contentPane.setBorder(new EmptyBorder(4, 4, 4, 4));
pages.add("Page01");
pages.add("Page02");
pages.add("Page03");
contentPane.add(new FirstPage(), "Page01");
contentPane.add(new SecondPage(), "Page02");
contentPane.add(new ThirdPage(), "Page03");
JPanel actionsPane = new JPanel(new GridBagLayout());
actionsPane.setBorder(new EmptyBorder(4, 4, 4, 4));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
actionsPane.add((first = new JButton("<< First")), gbc);
gbc.gridx++;
gbc.weightx = 1;
gbc.anchor = GridBagConstraints.WEST;
actionsPane.add((previous = new JButton("< Previous")), gbc);
gbc.gridx++;
gbc.anchor = GridBagConstraints.EAST;
actionsPane.add((next = new JButton("Next >")), gbc);
gbc.gridx++;
gbc.weightx = 0;
actionsPane.add((last = new JButton("Last >>")), gbc);
add(contentPane);
add(actionsPane, BorderLayout.SOUTH);
NavigationHandler handler = new NavigationHandler();
first.addActionListener(handler);
previous.addActionListener(handler);
next.addActionListener(handler);
last.addActionListener(handler);
gotoFirstPage();
}
protected void gotoFirstPage() {
currentPage = pages.get(0);
cardLayout.show(contentPane, currentPage);
}
protected void gotoPreviousPage() {
int index = pages.indexOf(currentPage);
index--;
if (index >= 0) {
currentPage = pages.get(index);
cardLayout.show(contentPane, currentPage);
}
}
protected void gotoNextPage() {
int index = pages.indexOf(currentPage);
index++;
if (index < pages.size()) {
currentPage = pages.get(index);
cardLayout.show(contentPane, currentPage);
}
}
protected void gotoLastPage() {
currentPage = pages.get(pages.size() - 1);
cardLayout.show(contentPane, currentPage);
}
protected class NavigationHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == first) {
gotoFirstPage();
} else if (e.getSource() == previous) {
gotoPreviousPage();
} else if (e.getSource() == next) {
gotoNextPage();
} else if (e.getSource() == last) {
gotoLastPage();
}
}
}
}
public class FirstPage extends JPanel {
public FirstPage() {
setLayout(new BorderLayout());
JLabel label = new JLabel("Page One");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
}
}
public class SecondPage extends JPanel {
public SecondPage() {
setLayout(new BorderLayout());
JLabel label = new JLabel("Page Two");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
}
}
public class ThirdPage extends JPanel {
public ThirdPage() {
setLayout(new BorderLayout());
JLabel label = new JLabel("Page Three");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
}
}
}
Example #3 - Model based
Or you could use a model based approach (which is probably more preferable), which defines the order in which components are displayed. For example
I need to have one JPanel opened on start. I have a button on that one to open to another JPanel which contains a button to bring me back. How do i write action listeners for those buttons. I have searched extensively. Do I need a JFrame? All examples seem to have it.
Regardless of which approach you might take, the basic idea is the same. You need to know where to go based on where you are...
To this end, this simple example uses a simple navigation interface to provide movement control for the panels and a List to maintain the order of the components.
You could just as simply use a queue of some kind, pushing the next panel onto it and popping the last panel of it as you switched views.
This is a quick and simple example of CardLayout
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class SwitchPanel extends JApplet{
private String currentView;
private List<String> viewNames;
#Override
public void init() {
final CardLayout cardLayout = new CardLayout();
setLayout(cardLayout);
Navigator navi = new Navigator() {
#Override
public void next() {
int index = viewNames.indexOf(currentView);
if (index > -1) {
index++;
if (index < viewNames.size()) {
currentView = viewNames.get(index);
cardLayout.show(getContentPane(), currentView);
}
}
}
#Override
public void previous() {
int index = viewNames.indexOf(currentView);
if (index > -1) {
index--;
if (index >= 0) {
currentView = viewNames.get(index);
cardLayout.show(getContentPane(), currentView);
}
}
}
};
MainPane mainPane = new MainPane(navi);
LastPane lastPane = new LastPane(navi);
viewNames = new ArrayList<>(2);
viewNames.add("main");
viewNames.add("last");
add(mainPane, "main");
add(lastPane, "last");
currentView = "main";
cardLayout.show(getContentPane(), "main");
}
public interface Navigator {
public void next();
public void previous();
}
public class MainPane extends JPanel {
private Navigator navigator;
public MainPane(Navigator navi) {
this.navigator = navi;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JButton btn = new JButton("Next >");
add(new JLabel("Main"), gbc);
add(btn, gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
navigator.next();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class LastPane extends JPanel {
private Navigator navigator;
public LastPane(Navigator navi) {
this.navigator = navi;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JButton btn = new JButton("< Previous");
add(new JLabel("Last"), gbc);
add(btn, gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
navigator.previous();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}