Java Swing: основной класс ожидает закрытия JFrame

Мне нужна помощь с простым приложением Java, которое использует два jframe для получения некоторых входных параметров. Вот набросок моего кода:

//second jframe, called when the button OK of the first frame is clicked
public class NewParamJFrame extends JFrame{
  ...
}

//first jframe
public class StartingJFrame extends JFrame{
  private static  NewParamJFrame newPFrame = null;
  private JTextField gnFilePath;
  private JButton btnOK;

  public StartingJFrame(){
            //..
    initComponents();
  }

  private void initComponents(){
     btnOK.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e){
        try{
        EventQueue.invokeAndWait(new Runnable(){
           public void run() {
               try {
               newPFrame = new NewParamJFrame();
               newPFrame.setVisible(true);
               } catch (Exception e) {
               e.printStackTrace();
               }
           }
         });
        }
        catch(InvocationTargetException e2) {} 
        catch(InterruptedException e1){}
        dispose();
      }
  }

  public String getText(){
       return gnFilePath.getText();
  }
}

public class Main {
  private static StartingJFrame begin = null;
  public static void main(String[] args) {
     try{
        EventQueue.invokeAndWait(new Runnable(){
            public void run() {
                try {
                    begin = new StartingJFrame();
                    begin.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    catch(InvocationTargetException e) {} 
    catch(InterruptedException e1){}

    String s= begin.getText();

    //...use s ...
  }
}

Вызов getText() вызывает исключение NullPointerException. Я хочу, чтобы основной класс дождался закрытия кадров, но я не знаю, как это сделать. Я использую свинг впервые.

6 ответов

Решение

Я хочу, чтобы основной класс дождался закрытия кадров, но я не знаю, как это сделать. Я использую свинг впервые.

Если я правильно понимаю вашу проблему, вам нужно StartingJFrame ждать до NewParamJFrame закрыт, а затем продолжить его выполнение. Если это так, то этого не произойдет, потому что JFrame не поддерживает модальность. Но JDialog делает, так что вы можете иметь только один JFrame и сделать запрос параметров в JDialog чей это родитель JFrame,

Чтобы лучше понять модальность, прочитайте раздел "Как использовать модальность в диалогах".

Также взгляните на эту тему: использование нескольких JFrames, хорошая / плохая практика?

В любом случае вы, вероятно, столкнетесь с новой проблемой: что JFrame делать, если пользователь закрывает / отменяет диалог без ввода какого-либо параметра? Как это могло JFrame знаете, что только что произошло в этом диалоге? Один подход описан в этом ответе. Вы увидите, что пример относится к диалоговому окну входа в систему, но проблема похожа на эту: Как диалоговое окно может уведомить свой родительский фрейм о том, как прошел процесс?

Самый простой способ дождаться закрытия без изменения потока кода - использовать модальный JDialog, Таким образом, вы должны изменить свой StartingJFrame класс, чтобы сделать его подклассом JDialog вместо JFrameи добавьте следующее в начало его конструктора:

super((Window)null);
setModal(true);

Тогда setVisible(true); вызов на StartingJFrame экземпляр будет ждать, пока диалоговое окно не будет закрыто и, следовательно, invokeAndWait вызов тоже будет ждать.

Вызов getText() вызывает исключение NullPointerException.

Так как, gnFilePath из JTextField является null,

private JTextField gnFilePath;

public String getText(){
       return gnFilePath.getText();// NullPointerException is throw here.
}

Избежать NPEнужно инициализировать JTextField а также JButton как ниже.

  private JTextField gnFilePath=new JTextField();
  private JButton btnOK=new JButton()

Попробуйте поставить это:

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

public class MyWindow extends JFrame{
    MyWindow(){
        setSize(300, 200);
        setLayout(null);    
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JButton b = new JButton("Close");
        b.setBounds((300-80)/2, (200-30)/2, 80, 30);
        //
        final MyWindow frame = this;
        b.addActionListener(
            new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent ev){
                    synchronized(frame){
                        frame.notify();
                    }
                
                    frame.setVisible(false);
                    frame.dispose();
                }
            }
        );
        //
        getContentPane().add(b);
        
        setVisible(true);
        
        synchronized(this){
            try{ 
                this.wait();
            }
            catch(InterruptedException ex){ }
        }
    }


    public static void main(String args[]) {
        new MyWindow();
        System.out.println("You are here");
    }
}

Код выше проверен.

Использование JDialog, вероятно, является самым простым решением, но в некоторых случаях желательно иметь JFrame, например, для отображения окна на панели задач. Использование механизма синхронизации, предложенного Octavio, является способом достижения этого, вот альтернатива с использованием CountDownLatch, блокирующего основной поток до тех пор, пока фрейм не будет закрыт:

      public static void main(String[] args) throws Exception {
    CountDownLatch latch = new CountDownLatch(1);

    SwingUtilities.invokeLater(() -> {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setSize(300, 200);
        frame.setVisible(true);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosed(WindowEvent e) {
                latch.countDown();
            }
        });
    });

    latch.await();
    System.out.println("Main thread released");
}

Вы можете использовать цикл (предпочтительно цикл do-while), чтобы удерживать кадр, пока другой кадр не закроется или не скроется. Обязательно разбивайте цикл или увеличивайте переменную, используемую для цикла, на определенную величину, когда другой кадр расположен или скрыт. Таким образом, вы можете сохранить свой StartingJFrame класс, чтобы остаться в качестве подкласса JFrame,

    do {
        if (changeLog.isVisible()) {
        } else {
            changeLog.dispose();
            break;
        }
    } while (hold < 1);

или же

    do {
        if (changeLog.isActive()) {
        } else {
            break;
        }
    } while (hold < 1);

Первый требует скрытия предыдущего кадра (JFrame.HIDE_ON_EXIT или же Window.setVisible(false)), прежде чем коды могут быть запущены. Последний требует, чтобы предыдущий кадр был "удален" (JFrame.DISPOSE_ON_EXIT или же (subclass of JFrame).dispose(), Добавьте любой из этих кодов на StartingJFrameТем не менее, так как вы создали NewParamJFrame в этом классе и соответствующие поля (ей) установлены в private,

Другие вопросы по тегам