Basically, in Swing CardLayout
allows you to switch between views within a single container. Start by taking a look at How to Use CardLayout for more details.
The following example use a Model-View-Controller approach and is intended to decouple of the code at the same time, so I'm afraid it's a little long winded, but that's a result of using the MVC and code separation approaches (I like coding to interfaces)
Lets start by defining the views we want...
public interface IView<C extends IViewController> {
public JComponent getView();
public C getViewController();
}
public interface ILoginView extends IView<ILoginViewController> {
}
public interface IWelcomeView extends IView<IWelcomeViewController> {
}
Obviously, we start with a base concept of a view and build on it. Each view has a controller, which dictates what each view is capable of doing...
public interface IViewController {
}
public interface ILoginViewController extends IViewController {
public void loginWasSuccessful(ICredentials credentials);
public void loginDidFail();
public void loginWasCancelled();
}
public interface IWelcomeViewController extends IViewController {
public ICredentials getCredentials();
public void setCredentials(ICredentials credentials);
public void setCredentialsListener(ICredentialsListener listener);
public ICredentialsListener getCredentialsListener();
public interface ICredentialsListener {
public void credentialsWereUpdated(ICredentials credentials);
}
}
Now obviously, we need to be able to pass some data between views (in this case the user details or "credentials")
public interface ICredentials {
public String getUserName();
}
Now, lets get to the nitty gritty and define the actual implementations of the views...
public abstract class AbstractView<C extends IViewController> extends JPanel implements IView<C> {
private C viewController;
public AbstractView(C viewController) {
this.viewController = viewController;
}
@Override
public JComponent getView() {
return this;
}
@Override
public C getViewController() {
return viewController;
}
}
public class WelcomeView extends AbstractView<IWelcomeViewController> implements IWelcomeView {
private JLabel userName;
public WelcomeView(IWelcomeViewController viewContoller) {
super(viewContoller);
viewContoller.setCredentialsListener((ICredentials credentials) -> {
userName.setText(credentials.getUserName());
revalidate();
repaint();
});
userName = new JLabel("...");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(10, 10, 10, 10);
add(new JLabel("WELCOME!"), gbc);
add(userName, gbc);
}
}
public class LoginView extends AbstractView<ILoginViewController> implements ILoginView {
private JTextField userName;
private JPasswordField password;
private JButton login;
private JButton cancel;
public LoginView(ILoginViewController controller) {
super(controller);
setLayout(new GridBagLayout());
userName = new JTextField(10);
password = new JPasswordField(10);
login = new JButton("Login");
cancel = new JButton("Cancel");
login.addActionListener((ActionEvent e) -> {
// Fake the login process...
// This might be handed off to another controller...
String name = userName.getText();
if (name != null && !name.isEmpty()) {
Random rnd = new Random();
if (rnd.nextBoolean()) {
getViewController().loginWasSuccessful(new DefaultCredentials(userName.getText()));
} else {
getViewController().loginDidFail();
}
}
});
cancel.addActionListener((ActionEvent e) -> {
getViewController().loginWasCancelled();
});
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(2, 2, 2, 2);
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("Login"), gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 1;
add(new JLabel("Username:"), gbc);
gbc.gridy++;
add(new JLabel("Password:"), gbc);
gbc.gridx++;
gbc.gridy = 1;
add(userName, gbc);
gbc.gridy++;
add(password, gbc);
JPanel controls = new JPanel();
controls.add(login);
controls.add(cancel);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(controls, gbc);
}
public class DefaultCredentials implements ICredentials {
private final String userName;
public DefaultCredentials(String userName) {
this.userName = userName;
}
@Override
public String getUserName() {
return userName;
}
}
}
Okay, now we have all that, we need to group it all together, through the CardLayout
, this is where the controllers come into play...
public class MainPane extends JPanel {
protected static final String LOGIN_VIEW = "View.login";
protected static final String WELCOME_VIEW = "View.welcome";
private CardLayout cardLayout;
private ILoginView loginView;
private IWelcomeView welcomeView;
public MainPane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
// This could be established via a factory or builder pattern
loginView = new LoginView(new LoginViewController());
welcomeView = new WelcomeView(new WelcomeViewController());
add(loginView.getView(), LOGIN_VIEW);
add(welcomeView.getView(), WELCOME_VIEW);
cardLayout.show(this, LOGIN_VIEW);
}
protected class LoginViewController implements ILoginViewController {
@Override
public void loginWasSuccessful(ICredentials credentials) {
welcomeView.getViewController().setCredentials(credentials);
cardLayout.show(MainPane.this, WELCOME_VIEW);
}
@Override
public void loginDidFail() {
JOptionPane.showMessageDialog(MainPane.this, "Login vaild", "Error", JOptionPane.ERROR_MESSAGE);
}
@Override
public void loginWasCancelled() {
SwingUtilities.windowForComponent(MainPane.this).dispose();
}
}
protected class WelcomeViewController implements IWelcomeViewController {
private IWelcomeViewController.ICredentialsListener credentialsListener;
private ICredentials credentials;
@Override
public ICredentials getCredentials() {
return credentials;
}
@Override
public void setCredentials(ICredentials credentials) {
this.credentials = credentials;
IWelcomeViewController.ICredentialsListener listener = getCredentialsListener();
if (listener != null) {
listener.credentialsWereUpdated(credentials);
}
}
@Override
public void setCredentialsListener(IWelcomeViewController.ICredentialsListener listener) {
this.credentialsListener = listener;
}
@Override
public IWelcomeViewController.ICredentialsListener getCredentialsListener() {
return credentialsListener;
}
}
}
This is pretty much the "core" Swing integration. With very little work, you could use the controller and view interfaces in JavaFX if you wanted to.
Now all this does is presents the login view to the user, if the user is successfully logged in, it will switch the views to the welcome view and pass the user credentials to it...
And in case you don't want to copy and paste all that, this is a simple runnable example I used to test it...
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestCardLayout {
public static void main(String[] args) {
new TestCardLayout();
}
public TestCardLayout() {
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 MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
protected static final String LOGIN_VIEW = "View.login";
protected static final String WELCOME_VIEW = "View.welcome";
private CardLayout cardLayout;
private ILoginView loginView;
private IWelcomeView welcomeView;
public MainPane() {
cardLayout = new CardLayout();
setLayout(cardLayout);
// This could be established via a factory or builder pattern
loginView = new LoginView(new LoginViewController());
welcomeView = new WelcomeView(new WelcomeViewController());
add(loginView.getView(), LOGIN_VIEW);
add(welcomeView.getView(), WELCOME_VIEW);
cardLayout.show(this, LOGIN_VIEW);
}
protected class LoginViewController implements ILoginViewController {
@Override
public void loginWasSuccessful(ICredentials credentials) {
welcomeView.getViewController().setCredentials(credentials);
cardLayout.show(MainPane.this, WELCOME_VIEW);
}
@Override
public void loginDidFail() {
JOptionPane.showMessageDialog(MainPane.this, "Login vaild", "Error", JOptionPane.ERROR_MESSAGE);
}
@Override
public void loginWasCancelled() {
SwingUtilities.windowForComponent(MainPane.this).dispose();
}