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
,