Search code examples
javaswingjframedisposedispatchevent

How to dispose a JFrame and recreate it as new without exiting the application?


I have a problem where I have to use two JFrames, so we can forget about good/bad practice.

Now that when we are past this point.

My Two JFrames are

  1. LoginFrame
  2. MainFrame

I want to sign out of MainFrame to LoginFrame and vice versa. In doing so, I want both the frames to open fresh. Just like how we instantiate them, i.e.

MainFrame.class

//on signout button event
new LoginFrame().setVisible(true);
dispose(); //it will dispose this frame and destroy its UI components and free resources etc etc.

Similarly,

LoginFrame.class

//on signin button event
new MainFrame().setVisible(true);
dispose(); //it will dispose this frame and destroy its UI components and free resources etc etc.

Now, this is working well but the problem is that I have a keyDispatchEvent in MainFrame which is executing a number of times I login after signout (without closing the application).

I believe that even after disposing, the JFrame it isn't actually disposed. So when I type any shortkey, previously disposed frame objects and new object of MainFrame are all dispatching their event. How can I lose ( I mean really dispose) all previous objects so that this event is only fired for current object?

MainFrame.class

final static KeyboardFocusManager keyManager=KeyboardFocusManager.getCurrentKeyboardFocusManager();

keyManager.addKeyEventDispatcher(new KeyEventDispatcher() 
{

        @Override
        public boolean dispatchKeyEvent(KeyEvent e) 
        {
           // if key is alt+r  
           {
               signout(); // getting called repeatedly on single key release 
           }
        }
});

What I don't want is using same objects because they will have previous state/information/changes. Just want to get rid of disposed objects.

Also, I have to work with KeyboardManager to DispatchKeyEvent in this case.


Solution

  • I hope the below code does what you want. Since you didn't post a minimal, reproducible example I can only guess what you want from your [poor] problem description and your insistence on [your] way or the highway.

    the problem is that I have a keyDispatchEvent in MainFrame which is executing a number of times I login after signout

    My guess is that you keep adding the same KeyEventDispatcher to the KeyboardFocusManager and each one that gets added will execute. So if you add the same KeyEventDispatcher three times, it will execute three times. I assume you only want to add it once and therefore you need to remove it when it is no longer required.

    I also assume that when you launch your application, you want the LoginFrame to be displayed first. Hence I added a main method to class LoginFrame, below.

    Class LoginFrame

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.awt.KeyboardFocusManager;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class LoginFrame extends JFrame implements ActionListener {
    
        public LoginFrame() {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(MainFrame.ked);
            setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
            JButton signin = new JButton("sign in");
            signin.addActionListener(this);
            JPanel panel = new JPanel();
            panel.add(signin);
            add(panel, BorderLayout.PAGE_END);
            pack();
            setLocationByPlatform(true);
        }
    
        @Override
        public void actionPerformed(ActionEvent event) {
            new MainFrame().setVisible(true);
            dispose();
        }
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> new LoginFrame().setVisible(true));
        }
    }
    

    Class MainFrame

    import java.awt.BorderLayout;
    import java.awt.KeyEventDispatcher;
    import java.awt.KeyboardFocusManager;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyEvent;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class MainFrame extends JFrame implements ActionListener {
        final static KeyboardFocusManager keyManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    
        private static boolean  done;
        static KeyEventDispatcher ked;
    
        public MainFrame() {
            keyManager.removeKeyEventDispatcher(ked);
            ked = new KeyEventDispatcher() {
                
                @Override
                public boolean dispatchKeyEvent(KeyEvent ke) {
                    int key = ke.getKeyCode();
                    int modifiers = ke.getModifiersEx();
                    if (key == KeyEvent.VK_R  &&  (modifiers & KeyEvent.ALT_DOWN_MASK) == KeyEvent.ALT_DOWN_MASK) {
                        if (done) {
                            done = false;
                        }
                        else {
                            done = true;
                            actionPerformed(null);
                        }
                    }
                    return false;
                }
            };
            keyManager.addKeyEventDispatcher(ked);
    
            setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
            JButton signout = new JButton("sign out");
            signout.addActionListener(this);
            JPanel panel = new JPanel();
            panel.add(signout);
            add(panel, BorderLayout.PAGE_END);
            pack();
            setLocationByPlatform(true);
        }
    
        @Override
        public void actionPerformed(ActionEvent event) {
            new LoginFrame().setVisible(true);
            dispose();
        }
    }
    

    NOTES:

    • I don't know why, but each time I hit Alt+R on the keyboard, the event dispatcher fires twice. Hence the done flag.