I have btnShowLibrary, which shows the books kept in the library. And then I have a button btnReturn that recreates previous JPanel with the btnShowLibrary among them.
Initializing contentPane:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new MigLayout("", "[124px,grow,fill][124px,grow,fill][124px,grow,fill]", "[30px,grow,fill][30px,grow,fill][30px,grow,fill][30px,grow,fill][30px,grow,fill][30px,grow,fill][30px,grow,fill]"));
btnShowLibrary:
JButton btnShowLibrary = new JButton("Show Library");
btnShowLibrary.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
contentPane.removeAll();
contentPane.add(new ShowLibrary().getPane());
contentPane.updateUI();
}
});
contentPane.add(btnShowLibrary, "cell 1 5");
ShowLibrary contentPane:
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new MigLayout("", "[grow]", "[grow,fill][grow][]"));
btnReturn:
Button btnReturn = new JButton("Return");
btnReturn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
contentPane.removeAll();
contentPane.add(new Library().getPane());
contentPane.updateUI();
}
});
contentPane.add(btnReturn, "cell 0 1,alignx center,aligny bottom");
Now for what happens:
Initialization
First click on the btnShowLibrary
First click on the btnReturn
2nd click on the bSL
2nd click on the bR
3rd click on the bSL
3rd click on the bR
From the pictures you can so how it's "moving" and I have no idea why. Can someone explain why this happens and suggest how to fix it?
An easy way to manage multiple panels is with the CardLayout layout. This layout allows you to show one of a number of panels at a time, making it easy to move from one to the other in a window. Here is a demo with some events passing between the GUI panels and the main "controller". Ideally, the GUI classes would be in their own files. See also https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html.
package Q47644243;
import java.awt.AWTEvent;
import java.awt.CardLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsConfiguration;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
/**
* Demonstrate multiple panels in a window using CardLayout
*
*/
@SuppressWarnings("serial")
public class MultiplePanels extends JFrame implements VetoableChangeListener {
public static void main(String[] args) {
new MultiplePanels();
}
// GUI components
private MainMenuPanel mainMenuPanel = null;
private DataEntryPanel dataEntryPanel = null;
private JPanel contentPanel;
private CardLayout cardLayout = new CardLayout();
final static String MENUPANEL = "Main Menu";
final static String DATAPANEL = "Enter Data";
// State variables
private int m_data = 0;
public MultiplePanels() {
try {
// Initialize
jbInit();
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
showCenterScreen(this);
setCursor(Cursor.getDefaultCursor());
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
private void jbInit() {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create the GUI panels
mainMenuPanel = new MainMenuPanel();
dataEntryPanel = new DataEntryPanel();
// Use property changes to communicate with the GUI
addPropertyChangeListener(DataEntryPanel.PROPERTY_Data, dataEntryPanel);
// Use vetoable changes to listen to the GUI
mainMenuPanel.addVetoableChangeListener(this);
dataEntryPanel.addVetoableChangeListener(this);
this.setTitle("Multiple Panes Example");
cardLayout.setHgap(5);
cardLayout.setVgap(5);
// Add the panels
contentPanel = (JPanel) this.getContentPane();
contentPanel.setLayout(cardLayout);
contentPanel.add(mainMenuPanel, MENUPANEL);
contentPanel.add(dataEntryPanel, DATAPANEL);
// Bring the menu to the front
cardLayout.show(contentPanel, MENUPANEL);
}
/**
* Receive events from the GUI and process them. There are two expected:<br>
* <ol>
* <li>a change to the total seconds field. This is validated and accepted or vetoed
* if the new value is not an integer >=0; and <br>
* <li>a request to perform the calculation.
* </ol>
*/
@Override
public void vetoableChange(PropertyChangeEvent pce)
throws PropertyVetoException {
// The GUI is signaling a change that the "controller/model" needs
// to deal with
if (DataEntryPanel.PROPERTY_Data.equals(pce.getPropertyName())) {
// Test to see if the data is valid
int newData = 1;
try {
newData = Integer.parseInt((String) pce.getNewValue());
} catch (NumberFormatException e) {
newData = -1;
}
if (newData <= 0) {
// Signals the GUI to reject the entered value.
throw new PropertyVetoException("Please enter a valid integer that is greater than zero.", pce);
}
// Save the state or data - bind with a database or
// some other action. Here we'll just update a "state" variable
m_data = newData;
}
// Data entry completed - validate and switch back to main menu
if (DataEntryPanel.PROPERTY_Done.equals(pce.getPropertyName())) {
if (m_data <= 0) {
// Request better data
throw new PropertyVetoException("Please enter a valid integer that is greater than zero.", pce);
}
// Switch back to the Main menu
cardLayout.show(contentPanel, MENUPANEL);
}
// Button pressed on the main menu
if (MainMenuPanel.PROPERTY_MenuButton.equals(pce.getPropertyName())) {
// This is a command to do something. Using a vetoable change event
// provides a nice mechanism to pass a message back to the GUI
// if required.
cardLayout.show(contentPanel, DATAPANEL);
}
}
/**
* Show in the center of the screen.
* (pack, set location and set visibility)
* @param window Window to position
*/
private void showCenterScreen(Window window) {
positionScreen (window);
window.setVisible(true);
window.toFront();
} // showCenterScreen
/**
* Position window in center of the screen
* @param window Window to position
*/
private void positionScreen (Window window)
{
window.pack();
// take into account task bar and other adornments
GraphicsConfiguration config = window.getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Dimension sSize = bounds.getSize();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
sSize.width -= (insets.left + insets.right);
sSize.height -= (insets.top + insets.bottom);
Dimension wSize = window.getSize();
// fit on window
if (wSize.height > sSize.height)
wSize.height = sSize.height;
if (wSize.width > sSize.width)
wSize.width = sSize.width;
window.setSize(wSize);
// Center
int x = (sSize.width - wSize.width) / 2;
int y = (sSize.height - wSize.height) / 2;
//
window.setLocation(bounds.x + x + insets.left, bounds.y + y + insets.top);
} // positionScreen
/*****************************************************************************/
/**
* A simple demo GUI for the a main menu with a button. This should be in its own file
* but for stack overflow, its presented as a subclass.
*
*/
public class MainMenuPanel extends JPanel implements ActionListener, PropertyChangeListener {
/**
* Constructor
*/
public MainMenuPanel () {
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public final static String PROPERTY_MenuButton = "MenuButton";
private GridBagLayout gridBagLayout = new GridBagLayout();
private JLabel lTitle = new JLabel();
private JButton bOption1 = new JButton();
private void jbInit() {
setLayout(gridBagLayout);
// Label
lTitle.setToolTipText("Click a Menu Button");
lTitle.setText("Main Menu");
// Menu Button
bOption1.setToolTipText("Show the Data Entry Panel");
bOption1.setText("Enter Data");
bOption1.addActionListener(this);
// Layout the fields
this.add(lTitle, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 2, 5), 0, 0));
this.add(bOption1, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0
,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(2, 5, 2, 5), 0, 0));
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == bOption1) {
try {
fireVetoableChange(PROPERTY_MenuButton,0,1);
} catch (PropertyVetoException e1) {
e1.printStackTrace();
}
}
}
// Property Change receives events from the controller
// Doesn't do anything in this case but could be used to
// change the menu based on state, show different entries
// etc...
@Override
public void propertyChange(PropertyChangeEvent pce) {
/* Example
if (PROPERTY_APropertyName.equals(pce.getPropertyName())) {
// do something relevant
}
*/
}
}
/*****************************************************************************/
/**
* A simple demo GUI for the a data entry with a button. This should be in its own file
* but for stack overflow, its presented as a subclass.
*
*/
public class DataEntryPanel extends JPanel implements ActionListener, PropertyChangeListener {
/**
* Constructor
*/
public DataEntryPanel () {
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
// Property definitions
public final static String PROPERTY_Done = "DoneButton";
public final static String PROPERTY_Data = "myData";
private GridBagLayout gridBagLayout = new GridBagLayout();
private JLabel lData = new JLabel();
private JFormattedTextField fData = new JFormattedTextField("0");
private JButton bDone = new JButton();
private String lastGoodData = "0";
private boolean m_settingValue;
private void jbInit() {
setLayout(gridBagLayout);
// Label
lData.setToolTipText("Enter a positive integer");
lData.setText("Enter number:");
// Field
fData.setText(lastGoodData);
fData.setEditable(true);
fData.setHorizontalAlignment(SwingConstants.RIGHT);
fData.addPropertyChangeListener("value", this);
fData.setColumns(15);
// Button
bDone.setToolTipText("Save the data and return to the menu.");
bDone.setText("Save/Done");
bDone.addActionListener(this);
// Layout the fields
this.add(lData, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
this.add(fData, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0
,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
//grab extra space when window is maximized
JPanel filler = new JPanel();
filler.setOpaque(false);
filler.setBorder(null);
this.add(filler, new GridBagConstraints(0, 1, 1, 1, 0.0, 1.0
,GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0));
this.add(bDone, new GridBagConstraints(1, 2, 2, 1, 0.0, 0.0
,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(15, 5, 10, 5), 0, 0));
}
@Override
public void actionPerformed(ActionEvent e) {
// Ignore button events while a field is setting its value
// This can happen if the field focus is lost to a button click
if (e.getSource() == bDone && !m_settingValue) {
try {
fireVetoableChange(PROPERTY_Done,0,1);
} catch (PropertyVetoException pve) {
JOptionPane.showMessageDialog(null,
pve.getMessage(), "Error Massage",
JOptionPane.ERROR_MESSAGE);
}
}
}
// Property Change receives events from the controller or fields
@Override
public void propertyChange(PropertyChangeEvent pce) {
m_settingValue = true;
// The "value" property signals a change in a field value
// Send the new value to the controller/model for validation
if (fData.equals(pce.getSource()) && "value".equals(pce.getPropertyName())) {
// Record the old value in case the change is vetoed. Don't use
// the pce.getOldValue(), it isn't always accurate.
String oldValue = lastGoodData;
String newValue = (String) pce.getNewValue();
try {
fireVetoableChange(PROPERTY_Data, oldValue, newValue);
lastGoodData = newValue;
} catch (PropertyVetoException e) {
JOptionPane.showMessageDialog(null,
e.getMessage(), "Error Massage",
JOptionPane.ERROR_MESSAGE);
// Reset fData outside of the "value" event thread/loop
// Otherwise the value is not updated to the oldValue
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
fData.setText(lastGoodData);
}
});
}
m_settingValue = false;
}
}
}
}