I have a CardLayout composed of numerous different JPanels which I show at will. This GUI is linked to a backend, and the purpose of the GUI is to allow the user to manipulate the data which is stored in the backend.
The problem is, when the user changes some data in the backend, my JLabels are not reflecting the change in that data. I understand that this is because the JLabels are initialized once, and when I update the variables that they are initialized to, I must call setText again in order for them to experience the change.
Example: Here is how my JLabel is initialized
CurrentBAName = new javax.swing.JLabel();
CurrentBAName.setText(GUI.currentBankAccount.getAccountName());
The problem is, when I change the value of GUI.currentBankAccount.getAccountName()
, the text on my JLabel does not update. I have JLabels like this all over the place, and it would be a pain to have to go through and manually call setText on all of them each time a user makes any kind of change. Is there any way that I can have the JLabel automatically update itself when the value of that variable changes?
Perhaps I could implement some sort of Observer pattern? How would I do this with JLabels?
Or maybe I could use a focusGained Event so that every time I switch to a panel, the focusGained event fires so that I can update things. I am not sure how to do this though, and I am not sure how focus works with CardLayout.
Maybe just a method that gets called every time I show a panel with layout.show(..)
. How would I implement this?
Is there a better way?
Update: Sample Code
GUI.java is my main GUI class and will consist of a JPanel to which all of my pages will be added and shown at will.
import javax.swing.*;
public class GUI extends javax.swing.JFrame
{
public static String item;
private javax.swing.JPanel MainPanel;
public GUI()
{
// creates a JPanel called MainPanel which will hold the cards
// in my CardLayout
initComponents();
}
private void initComponents()
{
MainPanel = new javax.swing.JPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
MainPanel.setBackground(new java.awt.Color(198, 118, 38));
MainPanel.setMaximumSize(new java.awt.Dimension(1000, 1000));
MainPanel.setName(""); // NOI18N
MainPanel.setPreferredSize(new java.awt.Dimension(1024, 768));
MainPanel.setLayout(new java.awt.CardLayout());
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(MainPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(MainPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
pack();
setLocationRelativeTo(null);
}
// Grab the panel that was created in initComponents() so that I can add
// other panels to it
public JPanel getMainPanel()
{
return MainPanel;
}
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
GUI mainGUI = new GUI();
// The JLabel in DisplayPanel.java will initially say root
item = "root";
JPanel cardHolder = mainGUI.getMainPanel();
//This panel contains a button which will update the name of
//the currentUserAccount
MainMenuPanel mainMenu = new MainMenuPanel(cardHolder);
cardHolder.add(mainMenu, "MainMenu");
//This panel holds a JLabel that I want to have updated automatically
//when the user presses the button on MainMenuPanel
DisplayPanel display = new DisplayPanel(cardHolder);
cardHolder.add(display, "Display");
mainGUI.pack();
mainGUI.setVisible(true);
}
});
}
}
DisplayPanel.java will be a JPanel with the JLabel that I want to be updated on it
import java.awt.*;
import javax.swing.*;
public class DisplayPanel extends javax.swing.JPanel
{
private JPanel MainPanel;
public DisplayPanel(JPanel MainPanel)
{
this.MainPanel = MainPanel;
initComponents(); // Creates my panel with the JLabels/buttons etc
}
@SuppressWarnings("unchecked")
private void initComponents()
{
jLabel1 = new javax.swing.JLabel();
GoToMainMenuButton = new javax.swing.JButton();
jLabel1.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N
jLabel1.setText(GUI.item); // sets the JLabel to the GUI.item variable
GoToMainMenuButton.setText("Go to MainMenuPanel");
GoToMainMenuButton.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(java.awt.event.MouseEvent evt)
{
GoToMainMenuButtonMouseClicked(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(269, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(GoToMainMenuButton)
.addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 145, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(266, 266, 266))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(168, 168, 168)
.addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 63, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(83, 83, 83)
.addComponent(GoToMainMenuButton)
.addContainerGap(116, Short.MAX_VALUE))
);
}
private void GoToMainMenuButtonMouseClicked(java.awt.event.MouseEvent evt)
{
// go back to the MainPanel
CardLayout layout = (CardLayout) (MainPanel.getLayout());
layout.show(MainPanel, "MainMenu");
}
private javax.swing.JButton GoToMainMenuButton;
private javax.swing.JLabel jLabel1;
}
MainMenuPanel.java will be a JPanel with a button on it which when pressed, will update the variable that the text of the JLabel in DisplayPanel is set to.
import java.awt.*;
import javax.swing.*;
public class MainMenuPanel extends javax.swing.JPanel
{
private JPanel MainPanel;
public MainMenuPanel(JPanel MainPanel)
{
this.MainPanel = MainPanel;
initComponents(); // Creates my panel with the JLabels/buttons etc
}
@SuppressWarnings("unchecked")
private void initComponents()
{
GoToDisplayPanelButton = new javax.swing.JButton();
GoToDisplayPanelButton.setText("Press Me");
GoToDisplayPanelButton.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(java.awt.event.MouseEvent evt)
{
GoToDisplayPanelButtonMouseClicked(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(252, 252, 252)
.addComponent(GoToDisplayPanelButton)
.addContainerGap(260, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(198, Short.MAX_VALUE)
.addComponent(GoToDisplayPanelButton)
.addGap(175, 175, 175))
);
}
private void GoToDisplayPanelButtonMouseClicked(java.awt.event.MouseEvent evt)
{
// Changes the value of item. I want this change to be reflected on my
// JLabel in DisplayPanel.java, but it is not because I do not explicitly
// call setText on the JLabel here. I am wondering if there is a way to
// have my JLabel automatically update when the variable in its setText
// is changed, without having to directly call setText. Or some other
// way to update it without calling setText every time I change something.
GUI.item = "new value";
// Switches to my DisplayPanel which should show the updated name in its JLabel
CardLayout layout = (CardLayout)(MainPanel.getLayout());
layout.show(MainPanel, "Display");
}
private javax.swing.JButton GoToDisplayPanelButton;
}
You need to use a notification system by having the view (the GUI) be notified by the model (the "backend") when data has been changed. Then the view can request the updated information and use it to update the JLabel display. The details of this will depend on the specifics of your code, but I often give my model (or a wrapper around the model) a SwingPropertyChangeSupport field as well as
addPropertyChangeListener(...)
and the similar remove listener methods, and then in the model's methods where a property is changed (a bound property), fire the SwingPropertyChangeSupport's notification method. For more details on an implementation, you should consider showing some details of your code, preferably a minimal example program.For example
Edit
Note that if all you want is notification of when show is called, you can always extend CardLayout and override its
show(...)
method. But if you do this, don't forget to call thesuper.show(...)
method as well.Edit
Note changes made to code indicated by
// !!