2

I've looked around online at how to open a new JFrame from an existing JFrame. I've found that apparently the best way to do this is dispose of the existing JFrame and open the new JFrame - however this is a problem.

I have a login form, one the users logs in, the login frame is disposed and the main frame is set visible.

import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class client {

    public static void main(String[] args) {
        initialize();
    }

    private static void initialize() {
        JFrame loginFrame = new JFrame("Login");
        loginFrame.setBounds(100, 100, 300, 300);
        loginFrame.setResizable(false);
        loginFrame.setLocationRelativeTo(null);
        loginFrame.setDefaultCloseOperation(loginFrame.HIDE_ON_CLOSE);
        loginFrame.getContentPane().setLayout(null);
        JFrame mainFrame = new JFrame("Main");
        mainFrame.setBounds(100, 100, 300, 197);
        mainFrame.setResizable(false);
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setDefaultCloseOperation(mainFrame.EXIT_ON_CLOSE);
        mainFrame.getContentPane().setLayout(null);
        JButton loginButton = new JButton("Login");
        loginButton.setBounds(102, 133, 89, 23);
        loginButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                loginButton.setEnabled(false);
                loginFrame.dispose();
                mainFrame.setVisible(true);
            }
        });
        loginFrame.getContentPane().add(loginButton);
        loginFrame.setVisible(true);
    }
}

However if the user launches the client and then decides not to login and closes it, the process remains running in the background?

I feel like this is a really stupid question and I am sorry if so, but I've looked around and couldn't find any workarounds for this. Am I ok to not dispose of the login frame and just hide it and set them both to EXIT_ON_CLOSE?

Thanks in advance!

Luke
  • 33
  • 4
  • disposing might close the JFrame, but it doesn't shut down your JVM. If you want that, System.exit(0); would do – Stultuske Aug 09 '18 at 10:12
  • Slightly different tack - The login "window" should be based on a `JDialog`, this allows the collection of the information from the user while halting the execution of the program at the point it's made visible. The authentication should then be carried out and a decision about where to go from there made. The biggest problem you have right know is you have two windows, which are hooked into the Event Dispatching Thread, even though one of them is not visible, it's ensuring that the EDT won't be terminated and the JVM will continue running – MadProgrammer Aug 09 '18 at 10:12
  • 1
    See [The Use of Multiple JFrames, Good/Bad Practice?](http://stackoverflow.com/q/9554636/418556) – Andrew Thompson Aug 09 '18 at 10:16

1 Answers1

2

So, the primary issue is, you have two frames, while not visible, both have been "realised", meaning that until ALL the application windows are disposed of, the Event Dispatching Thread won't exit, which means the JVM won't exit.

So, I suggest a slight change in approach. Rather then using two frames this way, the login "window" should be based on a modal dialog and the application frame shouldn't be created until you need it.

A modal dialg will stop the execution of the code at the point it's made visible in away that won't block the Event Dispatching Thread (it's black magic), this means you can use it a loop to keep prompting the user for credentials until they are either authenticated or the close/cancel the dialog.

I would also strongly encourage the use of JPanels as the base component, allowing the window based classes to just be containers, this isolates responsibility, decouples the code and makes for a more re-usable solution overall.

You can have a look at How to Make Dialogs for more details

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
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.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                LoginPane loginPane = new LoginPane();
                boolean authenticated = false;
                boolean exit = false;
                do {
                    int option = JOptionPane.showOptionDialog(null,
                                    loginPane,
                                    "login",
                                    JOptionPane.OK_CANCEL_OPTION,
                                    JOptionPane.PLAIN_MESSAGE,
                                    null,
                                    new Object[]{"Login", "Cancel"},
                                    "Login");
                    if (option == 0) {
                        // Authenticate
                        authenticated = true;
                    } else if (option == JOptionPane.CLOSED_OPTION || option == 1) {
                        exit = true;
                    }
                } while (!authenticated && !exit);
                if (authenticated) {
                    JFrame frame = new JFrame();
                    frame.add(new MainPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            }
        });
    }

    public class LoginPane extends JPanel {

        private JTextField userName;
        private JPasswordField password;

        public LoginPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;

            add(new JLabel("User name: "), gbc);
            gbc.gridy++;
            add(new JLabel("Password: "), gbc);

            gbc.gridx++;
            gbc.gridy = 0;

            userName = new JTextField(10);
            password = new JPasswordField(10);

            add(userName, gbc);
            gbc.gridy++;
            add(password, gbc);
        }

        public String getName() {
            return userName.getText();
        }

        public char[] getPassword() {
            return password.getPassword();
        }

    }

    public class MainPane extends JPanel {

        public MainPane() {
            setBorder(new EmptyBorder(50, 50, 50, 50));
            add(new JLabel("Super awesome app"));
        }

    }

}

I would also encourage DISPOSE_ON_CLOSE instead of HIDE_ON_CLOSE, it will release the native peer and remove the window from the applications window cache

Now, if you're really up for a more challenging methodology, you could have a look at Java and GUI - Where do ActionListeners belong according to MVC pattern?, which presents a MVC based login implementation

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366