Сон вызывает фрейм с невидимым содержимым

У меня есть рамка с меню строки меню, когда я выбираю пункт меню, я хочу, чтобы программа показывала сообщение, а затем через несколько секунд (с помощью функции сна) автоматически закрывала это сообщение.

Но вместо этого я получаю пустое сообщение (jdialog с невидимым содержимым):

введите здесь описание изображения

Если удалить закрывающее сообщение, его содержимое появится после времени сна.

Что мне нужно изменить, чтобы получить правильный результат?

Я хочу получить вот это:

введите здесь описание изображения

Полный рабочий код:

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.concurrent.TimeUnit;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;

public class MessageSleepTest extends JDialog {

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                new Frame("frame with menubar");
            }
        });
    }
}

class Message extends JDialog {

    public Message() {
        this.setLayout(new GridLayout(0, 1));
        this.add(new JLabel("Displaying this message for 3 seconds and then closing it...", JLabel.CENTER));

        this.setAlwaysOnTop(true);
        this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
}

class Frame extends JFrame{

    public Frame(String title){

        super(title);
        setJMenuBar(new MenuBar());
        setPreferredSize(new Dimension(500, 300));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null); // center the frame
        setVisible(true);

    }

}

class MenuBar extends JMenuBar implements ActionListener{
    public static JMenuItem itmOpen;

    public MenuBar() {
        JMenu menuFile = new JMenu("File");

        itmOpen = new JMenuItem("Open...");
        itmOpen.addActionListener(this);

        add(menuFile);
        menuFile.add(itmOpen);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JMenuItem source = (JMenuItem)e.getSource();

        if(source == itmOpen){

            JDialog message = new Message();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
            message.dispatchEvent(windowClosing);

        }
    }
}

person Andrei Sh    schedule 24.08.2017    source источник


Ответы (2)


У вас проблема с потоками:
sleep выполняется в потоке диспетчеризации событий AWT, который выполняет всю обработку событий в AWT и Swing. Поэтому, когда вы щелкаете по пункту меню, он создает объекты для Message, но застревает в методе setVisible(), который обычно отправляет событие в JDialog для размещения и отображения. Это сообщение попадает в очередь EDT, и после того, как ваш обработчик событий (метод actionPerformed) завершает работу, оно обрабатывается. Однако сон между ними двумя.

Так что попробуйте что-нибудь вроде этого:

@Override
public void actionPerformed(ActionEvent e) {
   JMenuItem source = (JMenuItem) e.getSource();
   if (source == itmOpen) {
      final JDialog message = new Message();
      new Thread( new Runnable() {
         @Override
         public void run() {
            try {
               TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException ex) {
               // Do nothing with it
            }
            WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
            message.dispatchEvent(windowClosing);
         }
      }).start();
   }
}
person Usagi Miyamoto    schedule 24.08.2017
comment
Большое спасибо за объяснение! Ваш метод работает. - person Andrei Sh; 24.08.2017

Еще одно решение с использованием swing.Timer

@Override
public void actionPerformed(ActionEvent e) {
    JMenuItem source = (JMenuItem)e.getSource();

    if(source == itmOpen){

        JDialog message = new Message();
        Timer timer = new Timer(1000 * 3, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
                message.dispatchEvent(windowClosing);
            }
        });
        timer.setRepeats(false);
        timer.start();

    }
}
person Andrei Sh    schedule 24.08.2017