0

I'm working on a java project, I handled every functionality but when it comes to GUI I'm a beginner. What I want to know is can i use java to display different scenes in one stage like in javaFX? e.g my starting point is a login panel after login empty the Jframe and display next veiw or scene. there are a lot of views what should i do?

HAMZA PERVEZ
  • 1
  • 1
  • 3

2 Answers2

3

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

        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
0

Well I'd suggest learning JavaFX if you want a different approach. You can load a StackPane into an FX scene (a scene is an FX object, not a scene like you're talking about) and then load another stackpane (your scene) into the first stackpane. When you wish to change scenes you just unload the stackpane from the first one and load a different one.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage window) {
        window.setTitle("Hello World!");
        root.getChildren().add(sceneOne);
        Scene scene = new Scene(root, 400, 400);
        window.setScene(scene);
        window.show();
    }

    private StackPane root = new StackPane();
    private MyFirstScene sceneOne = new MyFirstScene();
    private MySecondScene sceneTwo = new MySecondScene();

}

For your MyFirstScene (MySecondScene) you just extend StackPane and you can add all the elements you wish. The reason I'm suggesting FX is because Swing is a bit depreciated and Oracle made FX to replace it. I'd get familiar with it if you're going to be making many GUIs using Java.

Philip Vaughn
  • 606
  • 6
  • 20
  • *"Oracle made FX to replace it"* - No, they didn't, Sun made it to compete against Flash, but HTML 5 killed both and instead of wanting to waste their time, they tacked on support for things like table and tree components, which were missing in version 1 (and dropping the requirement for the fxxml) and have tacked on 3D support in version 3 (fx8), so I'm not sure anybody really knows what fx is suppose to use for. The lack of inbuilt support for system look and feels is the killer for me, but that's my experience – MadProgrammer Apr 14 '15 at 20:54
  • http://www.oracle.com/technetwork/java/javafx/overview/faq-1446554.html Point 6 is the one of interest. I'm not here to argue I'm just saying the amount of code required to switch application pages in FX is much more precise and easy to follow. – Philip Vaughn Apr 15 '15 at 21:27
  • Sure, but it's not why it was "created", it's a decision that was made after the fact when it failed to meet its original expectations. I have no issue with fx, I have issue with people who suggest its original intention was to replace Swing (especially because Swing is "hard" - any new API takes time to learn), when it wasn't. And I'm not sure that CardLayout.show(Container, String) is a lot of code ;) – MadProgrammer Apr 15 '15 at 21:33
  • Also giving job posting trends, I'm not sure that Swing or fx have that bright a future, web based technologies seem to be the trend (as well as mobile based Apis :P) – MadProgrammer Apr 15 '15 at 21:40
  • Your last comment is actually probably true. Java has failed to create a GUI platform that is useful. Whenever people think of Java they think of a platform that is used for server-based applications. I work at Home Depot and all of our store systems are Java there, however you never see games programmed in Java mainly C++. – Philip Vaughn Apr 15 '15 at 21:45