Java : Swing : Hide frame after button pressed

后端 未结 4 610
闹比i
闹比i 2021-01-14 17:37

I have a button in a java frame that when pressed it reads a value from a text field and uses that string as a port name attempting to connect to a serial device.

If

相关标签:
4条回答
  • 2021-01-14 18:11

    Your code would be much more readable if you named JFrame instances xxxFrame, and JPanel instances xxxPanel. Naming JPanel instances xxxFrame makes things very confusing.

    It would also help if you pasted the stack trace of the exception.

    I suspect the problem comes from the fact that frame is null. This is due to the fact that the frame field is only initialized in the createAndShowGUI method, but this method doesn't display the current connection panel, but a new one, which thus have a null frame field:

    ConnectionFrame firstPanel = new ConnectionFrame();
    // The firstPanel's frame field is null
    firstPanel.createAndShowGUI();
    // the firstPanel's frame field is now not null, but
    // the above call opens a JFrame containing another, new ConnectionFrame, 
    // which has a null frame field
    

    The code of createAndShowGUI should contain

    frame.add(this);
    

    rather than

    frame.add(new ConnectionFrame());
    
    0 讨论(0)
  • 2021-01-14 18:12

    Running your snippet (after removing/tweaking around the custom classes), throws an NPE. Reason is that the frame you'r accessing is null. And that's because it's never set. Better not rely on any field, let the button find its toplevel ancestor and hide that, like in

            public void actionPerformed(final ActionEvent e) {
    
                boolean success = true;
                if (success == false) {
                    System.out.println("Could not connect");
                    return;
                }
    
                Window frame = SwingUtilities.windowForComponent((Component) e
                        .getSource());
                frame.setVisible(false); //no problem :-)
    
            }
    
    0 讨论(0)
  • 2021-01-14 18:25

    Your problem is with this line:

      frame.add(new ConnectionFrame());
    

    You're creating a new ConnectionFrame object, and so the frame that your button tries to close on is not the same as the one being displayed, and this is the source of your problem.

    If you change it to,

      //!! frame.add(new ConnectionFrame());
      frame.add(this);
    

    so that the two JFrames are one and the same, things may work more smoothly.

    But having said that, your whole design smells bad and I'd rethink it in a more OOP and less static fashion. Also, use dialogs where dialogs are needed, not frames, and rather than dialogs consider swapping views (JPanels) via CardLayout as a better option still.

    Myself, I'd create a "dumb" GUI for this, one that creates a JPanel (here in my example it extends a JPanel for simplicity, but I'd avoid extending if not necessary), and I'd let whoever is calling this code decide what to do with the information via some control. For e.g.,

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class ConnectionPanel extends JPanel {
    
       private JTextField textField;
       private JButton connectButton;
       private ConnectionPanelControl control;
    
       public ConnectionPanel(final ConnectionPanelControl control) {
          super(new GridBagLayout());
          this.control = control;
    
          ActionListener listener = new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                if (control != null) {
                   control.connectButtonAction();
                }
             }
          };
    
          textField = new JTextField(14);
          textField.addActionListener(listener);
          textField.setText("/dev/ttyUSB0");
    
          connectButton = new JButton("Connect");
    
          GridBagConstraints c = new GridBagConstraints();
          c.gridwidth = GridBagConstraints.REMAINDER;
    
          c.fill = GridBagConstraints.HORIZONTAL;
          add(textField, c);
    
          c.fill = GridBagConstraints.BOTH;
          c.weightx = 1.0;
          c.weighty = 1.0;
          add(connectButton, c);
    
          connectButton.addActionListener(listener);
       }
    
       public String getFieldText() {
          return textField.getText();
       }
    
    }
    

    Again, something outside of the simple GUI would make decisions on what to do with the text that the textfield contains and what to do with the GUI that is displaying this JPanel:

    public interface ConnectionPanelControl {
    
       void connectButtonAction();
    
    }
    

    Also, you will likely do any connecting in a background thread so as to not freeze your GUI, probably a SwingWorker. Perhaps something like this:

    import java.awt.event.ActionEvent;
    import java.util.concurrent.ExecutionException;
    
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class MyMain extends JPanel {
       public MyMain() {
          add(new JButton(new ConnectionAction("Connect", this)));
       }
    
       private static void createAndShowGui() {
          JFrame frame = new JFrame("My Main");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.getContentPane().add(new MyMain());
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }
    
    @SuppressWarnings("serial")
    class ConnectionAction extends AbstractAction {
       private MyMain myMain;
       private ConnectionPanel cPanel = null;
       private JDialog dialog = null;
    
       public ConnectionAction(String title, MyMain myMain) {
          super(title);
          this.myMain = myMain;
       }
    
       @Override
       public void actionPerformed(ActionEvent e) {
          if (dialog == null) {
             dialog = new JDialog(SwingUtilities.getWindowAncestor(myMain));
             dialog.setTitle("Connect");
             dialog.setModal(true);
             cPanel = new ConnectionPanel(new ConnectionPanelControl() {
    
                @Override
                public void connectButtonAction() {
                   final String connectStr = cPanel.getFieldText();
                   new MySwingWorker(connectStr).execute();
                }
             });
             dialog.getContentPane().add(cPanel);
             dialog.pack();
             dialog.setLocationRelativeTo(null);
          }
          dialog.setVisible(true);
       }
    
       private class MySwingWorker extends SwingWorker<Boolean, Void> {
          private String connectStr = "";
    
          public MySwingWorker(String connectStr) {
             this.connectStr = connectStr;
          }
    
          @Override
          protected Boolean doInBackground() throws Exception {
             // TODO: make connection and then return a result
             // right now making true if any text in the field
             if (!connectStr.isEmpty()) {
                return true;
             }
             return false;
          }
    
          @Override
          protected void done() {
             try {
                boolean result = get();
                if (result) {
                   System.out.println("connection successful");
                   dialog.dispose();
                } else {
                   System.out.println("connection not successful");
                }
             } catch (InterruptedException e) {
                e.printStackTrace();
             } catch (ExecutionException e) {
                e.printStackTrace();
             }
          }
       }
    }
    
    0 讨论(0)
  • 2021-01-14 18:30

    for Swing GUI is better create only once JFrame and another Top-Level Containers would be JDialog or JWindow(un-decorated by default),

    simple example here

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class SuperConstructor extends JFrame {
    
        private static final long serialVersionUID = 1L;
    
        public SuperConstructor() {
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setPreferredSize(new Dimension(300, 300));
            setTitle("Super constructor");
            Container cp = getContentPane();
            JButton b = new JButton("Show dialog");
            b.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent evt) {
                    FirstDialog firstDialog = new FirstDialog(SuperConstructor.this);
                }
            });
            cp.add(b, BorderLayout.SOUTH);
            JButton bClose = new JButton("Close");
            bClose.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent evt) {
                    System.exit(0);
                }
            });
            add(bClose, BorderLayout.NORTH);
            pack();
            setVisible(true);
        }
    
        public static void main(String args[]) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    SuperConstructor superConstructor = new SuperConstructor();
                }
            });
        }
    
        private class FirstDialog extends JDialog {
    
            private static final long serialVersionUID = 1L;
    
            FirstDialog(final Frame parent) {
                super(parent, "FirstDialog");
                setPreferredSize(new Dimension(200, 200));
                setLocationRelativeTo(parent);
                setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
                JButton bNext = new JButton("Show next dialog");
                bNext.addActionListener(new ActionListener() {
    
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        SecondDialog secondDialog = new SecondDialog(parent, false);
                    }
                });
                add(bNext, BorderLayout.NORTH);
                JButton bClose = new JButton("Close");
                bClose.addActionListener(new ActionListener() {
    
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        setVisible(false);
                    }
                });
                add(bClose, BorderLayout.SOUTH);
                pack();
                setVisible(true);
            }
        }
        private int i;
    
        private class SecondDialog extends JDialog {
    
            private static final long serialVersionUID = 1L;
    
            SecondDialog(final Frame parent, boolean modal) {
                //super(parent); // Makes this dialog unfocusable as long as FirstDialog is visible
                setPreferredSize(new Dimension(200, 200));
                setLocation(300, 50);
                setModal(modal);
                setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                setTitle("SecondDialog " + (i++));
                JButton bClose = new JButton("Close");
                bClose.addActionListener(new ActionListener() {
    
                    @Override
                    public void actionPerformed(ActionEvent evt) {
                        setVisible(false);
                    }
                });
                add(bClose, BorderLayout.SOUTH);
                pack();
                setVisible(true);
            }
        }
    }
    

    better would be re-use Top-Level Containers, as create lots of Top-Level Containers on Runtime (possible memory lack)

    0 讨论(0)
提交回复
热议问题