У меня есть простой диалог JFrame, в котором должно отображаться сообщение «Пожалуйста, подождите», пока основная программа выполняет другие действия (что может занять от 30 секунд до более 10 минут):
public JFrame waitDialog() {
JFrame wait = new JFrame();
wait.setTitle("My Dialog");
wait.setAlwaysOnTop(true);
wait.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
wait.setSize(300, 150);
wait.setLocationRelativeTo(null);
wait.setLayout(new BorderLayout());
String msg = "Plase Wait...";
wait.add(new JLabel(msg), BorderLayout.CENTER);
return wait;
}
Когда я вызываю его в ходе программы, он работает нормально:
...
JFrame wait = waitDialog();
wait.setVisisble(true);
for (int i=0;i<1000;i++){
// Other activities
System.out.println(i);
}
wait.setVisisble(false);
...
Однако, если я использую выполнение этого диалогового окна через ActionEvent (я нажимаю кнопку «Выполнить другие действия» в JFrame основного приложения), даже если я использую ТОЧНО ЖЕ КОД для вызова waitDialog(), диалоговое окно отображается без какого-либо текста. Он отображает пустой диалог без сообщения «Пожалуйста, подождите...»:
public void actionPerformed(ActionEvent action){
if (action.getSource==waitforit) {
runWait();
}
Public void runWait() {
JFrame wait = waitDialog();
wait.setVisisble(true);
for (int i=0;i<1000;i++){
// Other activities
System.out.println(i);
}
wait.setVisisble(false);
}
Я тщательно переработал это и не могу понять, почему текст JPanel не отображается ТОЛЬКО тогда, когда я вызываю диалог через ActionEvent.
Что мне здесь не хватает?
После дополнительных исследований я вижу, что этот комментарий от @g00se является правильным ответом, но я не могу его выбрать, поскольку он не опубликован в качестве ответа.
Действие (например, нажатие кнопки) выполняется специальным потоком AWT-EventQueue. Тот самый поток, который отвечает за отрисовку всего кадра. Если вы позволите функции завершить рисование вашего JFrame и закрыть его после задержки в другом потоке, ваш код должен работать.
public void runWait() {
JFrame wait = waitDialog();
wait.setVisible(true);
closeFrameDelayed(wait);
}
static void closeFrameDelayed(JFrame frame) {
new Thread(() -> {
try {
Thread.sleep(2000); // Wait 2 seconds
} catch (InterruptedException ex) {
// No error handling (or printing) is bad error handling,
// but that is out of scope here, i guess
ex.printStackTrace(System.out);
} finally {
// SwingUtilities.invokeLater pushes the comand to the AWT-EventQueue
// This is required, because AWT/Swing methods are not thread safe
SwingUtilities.invokeLater(() -> {
frame.setVisible(false);
});
}
}).start();
}
При этом создавать несколько фреймов, как правило, не очень хорошая идея (см. Использование нескольких JFrames: хорошая или плохая практика?).
Как утверждает g00se в комментарии, основная проблема заключается в том, что вы выполняете тяжелые, блокирующие задачи в потоке пользовательского интерфейса. Попробуйте выполнить логику медленной «активности» в отдельном потоке и вызовите SwingUtilities.invokeLater(() -> frame.setVisible(false));
, когда это будет сделано.
Вместо этого лучше используйте JDialog. На самом деле использование JDialog
может по иронии судьбы показать реальную ситуацию. Потому что тогда «Другие действия» не будут выполняться, пока диалог не закроется ;)
@ g00se это всего лишь опция (хотя и по умолчанию). setModal(false)
делает свое дело. См. docs.oracle.com/javase/8/docs/api/java/awt/… или docs.oracle.com/javase/tutorial/uiswing/misc/modality.html для получения более новых версий Java.
Да, я знаю setModal
, и я предвидел, что вы это процитируете;) Дело в том, что ваш ответ работает против правильной координации потоков, поскольку тяжелые «Другие действия» в любом случае не должны выполняться в основном потоке. . Они должны выполняться в выделенном потоке, который координируется с потоком диспетчеризации событий. Ваш подход «Подожди 2 секунды» совершенно произволен и нескоординирован.
@ g00se Вы правы, рекомендация JDialog без знания точного контекста может ввести в заблуждение. Я удалил эту строку и добавил подсказку по основной проблеме с «другими действиями» в очереди событий. Я согласен, что «подождите 2 секунды» — это произвольно. Это служит просто примером.
Всё равно так делать нельзя. У вас неполное представление о том, как работает потоковая обработка Swing. Взгляните на прекрасно документированный SwingWorker, и он даст вам несколько идей о том, как добиться того, чего вы хотите.
Всё равно так делать нельзя. У вас неполное представление о том, как работает потоковая обработка Swing. Взгляните на прекрасно документированную версию
SwingWorker
, и она даст вам некоторые идеи о том, как добиться того, чего вы хотите.