Java Application, with multiple Scenes

Solution 1:

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)

CardLayout

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();
            }

        }

        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;
            }

        }

    }

    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);

        }

    }

    public interface ICredentials {

        public String getUserName();
    }

    public interface IView<C extends IViewController> {

        public JComponent getView();

        public C getViewController();

    }

    public interface ILoginView extends IView<ILoginViewController> {
    }

    public interface IWelcomeView extends IView<IWelcomeViewController> {
    }

    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;
            }

        }

    }

}