【问题标题】:Java : Swing : Hide frame after button pressedJava:Swing:按下按钮后隐藏框架
【发布时间】:2011-11-12 04:09:20
【问题描述】:

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

如果此连接成功,则该方法返回 true,否则返回 false。如果它返回 true,我希望框架消失。然后会出现一系列在其他类中指定的其他框架,其中包含用于控制串行设备的选项。

我的问题是:按钮连接到动作侦听器,按下时会调用此方法。如果我尝试使用 frame.setVisible(true);方法 java 抛出一个抽象按钮错误,因为我有效地告诉它在按钮按下方法退出之前消失包含按钮的框架。移除 frame.setVisible(true);允许程序正确运行,但是我留下了一个不再使用的挥之不去的连接框架。

如何让框架在按下按钮后消失?

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


}

}

【问题讨论】:

  • 应用程序不太可能。应该使用多个JFrame。小的 UI 元素可能会在 JDialogJOptionPane 中弹出,而 CardLayout 或各种 Swing 组件可用于在容器中包含多个 UI(或“屏幕”)。
  • 如需尽快获得更好的帮助,请发帖SSCCE

标签: java swing actionlistener


【解决方案1】:

运行您的 sn-p(在删除/调整自定义类之后)会引发 NPE。原因是您访问的框架为空。那是因为它从未设置过。最好不要依赖任何字段,让按钮找到它的顶级祖先并隐藏它,就像在

        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 :-)

        }

【讨论】:

  • 这对我有帮助,谢谢。然而,我有一个问题:这种方法更安全吗?致电SwingUtilities.windowForComponent(...) 时,这种方法有什么已知的缺陷吗?就我而言,我有一个应用程序,其中主框架 (JFrame) 包含多个 JPanels,所以在我实施更多之前,我想知道会发生什么。提前致谢。
【解决方案2】:

你的问题在于这一行:

  frame.add(new ConnectionFrame());

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

如果你改成,

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

这样两个JFrame是一体的,事情可能会更顺利。

但是话虽如此,您的整个设计都闻起来很糟糕,我会以更面向对象和更少静态的方式重新考虑它。此外,在需要对话框而不是框架的地方使用对话框,而不是对话框,考虑通过 CardLayout 交换视图 (JPanels) 作为更好的选择。

我自己,我会为此创建一个“哑”GUI,它会创建一个 JPanel(在我的示例中,为了简单起见,它扩展了一个 JPanel,但如果没有必要,我会避免扩展),我会让调用此代码的人可以通过某种控制来决定如何处理这些信息。例如,

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

}

同样,简单 GUI 之外的东西会决定如何处理文本字段包含的文本以及如何处理显示此 JPanel 的 GUI:

public interface ConnectionPanelControl {

   void connectButtonAction();

}

此外,您可能会在后台线程中进行任何连接,以免冻结您的 GUI,可能是 SwingWorker。也许是这样的:

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

【讨论】:

  • 非常感谢所有快速回复的人。我发布了这个问题,然后离开了 15 分钟,我想如果幸运的话,可能会有 1 条回复……结果有 4 条!
【解决方案3】:

如果您将 JFrame 实例命名为 xxxFrame,并将 JPanel 实例命名为 xxxPanel,您的代码将更具可读性。将 JPanel 实例命名为 xxxFrame 会使事情变得非常混乱。

如果您粘贴异常的堆栈跟踪也会有所帮助。

我怀疑问题来自框架为空的事实。这是因为 frame 字段只在 createAndShowGUI 方法中进行了初始化,但是这个方法并没有显示当前的连接面板,而是一个新的,因此有一个空的 frame 字段:

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

createAndShowGUI 的代码应该包含

frame.add(this);

而不是

frame.add(new ConnectionFrame());

【讨论】:

    【解决方案4】:

    对于 Swing GUI,最好只创建一次 JFrame 而另一个 Top-Level Containers 将是 JDialogJWindow(默认未装饰),

    这里是一个简单的例子

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

    最好重用顶级容器,因为在运行时创建大量顶级容器(可能内存不足)

    【讨论】:

      猜你喜欢
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 1970-01-01
      • 2017-01-31
      • 2021-03-21
      • 1970-01-01
      相关资源
      最近更新 更多