简体   繁体   English

Java:Swing:按下按钮后隐藏框架

[英]Java : Swing : Hide frame after button pressed

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. 我在java框架中有一个按钮,按下它时会从文本字段中读取一个值,并将该字符串用作尝试连接到串行设备的端口名称。

If this connection is successful the method returns true if not it returns false. 如果此连接成功,则该方法返回true,否则返回false。 If it returns true I want the frame to disappear. 如果它返回true,我希望框架消失。 A series of other frames specifed in other classes will then appear with options to control the serial device. 然后将出现在其他类中指定的一系列其他帧以及控制串行设备的选项。

My problem is: the button is connected to an action listener, when pressed this method is invoked. 我的问题是:按钮连接到动作监听器,按下此方法被调用。 If I try to use the frame.setVisible(true); 如果我尝试使用frame.setVisible(true); method java throws a abstract button error because I'm effectively telling it to disappear the frame containing the button before the button press method has exited. 方法java抛出一个抽象按钮错误,因为我有效地告诉它在按钮按下方法退出之前消失包含按钮的框架。 Removing the frame.setVisible(true); 删除frame.setVisible(true); allow the program to run correctly however I am left with a lingering connection frame that is no longer any use. 允许程序正确运行但是我留下了一个不再有用的延迟连接框架。

How to I get the frame to disappear upon pressing a the button? 按下按钮后如何让框架消失?

package newimplementation1;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


/**
 *
 * @author Zac
 */

public class ConnectionFrame extends JPanel implements ActionListener {


private JTextField textField;
private JFrame frame;
private JButton connectButton;
private final static String newline = "\n";

public ConnectionFrame(){

    super(new GridBagLayout());

    textField = new JTextField(14);
    textField.addActionListener(this);
    textField.setText("/dev/ttyUSB0");

    connectButton = new JButton("Connect");

    //Add Components to this panel.
    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(new ActionListener() {

        public void actionPerformed(ActionEvent e)
        {

            boolean success = Main.mySerialTest.initialize(textField.getText());

            if (success == false) {System.out.println("Could not connect"); return;}

            frame.setVisible(false);  // THIS DOES NOT WORK!!

            JTextInputArea myInputArea = new JTextInputArea();
            myInputArea.createAndShowGUI();

            System.out.println("Connected");


        }
    });

}

    public void actionPerformed(ActionEvent evt) {

            // Unimplemented required for JPanel

    }

    public void createAndShowGUI() {

    //Create and set up the window.
    frame = new JFrame("Serial Port Query");
    frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);


    //Add contents to the window.
    frame.add(new ConnectionFrame());
    frame.setLocation(300, 0);


    //Display the window.
    frame.pack();
    frame.setVisible(true);

            frame.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentHidden(ComponentEvent e) {
            System.out.println("Exiting Gracefully");
            Main.mySerialTest.close();
            ((JFrame)(e.getComponent())).dispose();
            System.exit(0);
        }
    });


}

}

Running your snippet (after removing/tweaking around the custom classes), throws an NPE. 运行你的代码片段(在删除/调整自定义类之后),抛出一个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 :-)

        }

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. 您正在创建一个新的ConnectionFrame对象,因此您的按钮尝试关闭的框架与显示的框架不同,这就是您的问题的根源。

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. 这样两个JFrame是同一个,事情可能会更顺利。

But having said that, your whole design smells bad and I'd rethink it in a more OOP and less static fashion. 但话说回来,你的整个设计闻起来很糟糕,我会以更多的OOP和更少静态的方式重新思考它。 Also, use dialogs where dialogs are needed, not frames, and rather than dialogs consider swapping views (JPanels) via CardLayout as a better option still. 此外,使用需要对话框的对话框,而不是框架,而不是对话框考虑通过CardLayout交换视图(JPanels)作为更好的选择。

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. 我自己,我为此创建了一个“哑”的GUI,创建一个JPanel(在我的例子中,为了简单起见,它扩展了JPanel,但如果没有必要,我会避免扩展),我会让任何人都在调用此代码通过某种控制决定如何处理信息。 For eg, 例如,

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: 同样,简单GUI之外的内容将决定如何处理文本字段包含的文本以及如何处理显示此JPanel的GUI:

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. 此外,您可能会在后台线程中进行任何连接,以便不冻结您的GUI,可能是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();
         }
      }
   }
}

Your code would be much more readable if you named JFrame instances xxxFrame, and JPanel instances xxxPanel. 如果您将JFrame实例命名为xxxFrame,并将JPanel实例命名为xxxPanel,那么您的代码将更具可读性。 Naming JPanel instances xxxFrame makes things very confusing. 命名JPanel实例xxxFrame会让事情变得非常混乱。

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. 我怀疑问题来自于frame为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: 这是因为frame字段仅在createAndShowGUI方法中初始化,但此方法不显示当前连接面板,而是显示新的连接面板,因此具有空框架字段:

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 createAndShowGUI的代码应该包含

frame.add(this);

rather than 而不是

frame.add(new ConnectionFrame());

for Swing GUI is better create only once JFrame and another Top-Level Containers would be JDialog or JWindow (un-decorated by default), 对于Swing GUI最好只创建一次JFrame和另一个顶级容器将是JDialogJWindow (默认情况下未装饰),

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) 更好的是重新使用顶级容器,因为在运行时创建了许多顶级容器(可能缺少内存)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM