Java: Swing: скрыть кадр после нажатия кнопки - PullRequest
3 голосов
/ 07 сентября 2011

У меня есть кнопка в кадре 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);
        }
    });


}

}

Ответы [ 4 ]

5 голосов
/ 07 сентября 2011

Запустив ваш фрагмент (после удаления / настройки пользовательских классов), бросает 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 :-)

        }
4 голосов
/ 07 сентября 2011

Ваша проблема с этой строкой:

  frame.add(new ConnectionFrame());

Вы создаете новый объект ConnectionFrame, и поэтому кадр, на котором пытается закрыть ваша кнопка, не совпадает с отображаемым, иэто источник вашей проблемы.

Если вы измените его на

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

, чтобы два JFrames были одним и тем же, все могло бы работать более плавно.

Но, сказав это, весь ваш дизайн пахнет плохо, и я бы переосмыслил его более ООП и менее статично.Кроме того, используйте диалоги, в которых нужны диалоги, а не фреймы, а не диалоги, и все же лучше поменяйте местами представления (JPanels) с помощью CardLayout.тот, который создает 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 будет принимать решения о том, что делать с текстом, содержащимся в текстовом поле, и что делать с GUI, отображающим этот JPanel:

public interface ConnectionPanelControl {

   void connectButtonAction();

}

Кроме того, вы, скорее всего, будете выполнять любые подключения в фоновом потоке, чтобы не заморозить графический интерфейс, возможно, 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();
         }
      }
   }
}
1 голос
/ 07 сентября 2011

для Swing GUI лучше создавать только один раз JFrame, а другие Контейнеры верхнего уровня будут JDialog или JWindow (без декорации по умолчанию),

простопример здесь

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

лучше было бы повторно использовать контейнеры верхнего уровня, так как создает много контейнеров верхнего уровня во время выполнения (возможный недостаток памяти)

1 голос
/ 07 сентября 2011

Ваш код был бы намного более читабельным, если бы вы назвали экземпляры JFrame xxxFrame и экземпляры JPanel xxxPanel.Присвоение имен экземплярам JPanel xxxFrame очень усложняет ситуацию.

Также было бы полезно, если бы вы вставили трассировку стека исключения.

Я подозреваю, что проблема связана с тем, что фрейм имеет значение null.Это связано с тем, что поле 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());
...