JDialog不可见,组件可点击

时间:2013-12-09 07:37:51

标签: java multithreading swing

我在启动应用程序时使用JDialog实例化,以多次显示消息。有时,对话框及其控件是不可见的,但可以单击。

JDialog仅实例化一次,并且每次显示一条消息时设置为可见'true',然后设置为可见'false',直到显示下一条消息为止。

为了排除多线程相关问题,当线程创建消息并显示对话框时,我总是使用SwingUtilities.invokeLater(...)进行ui调用。

因为它是一个庞大的项目而且我的问题与任何特定代码无关,所以我不会发布代码但会描述问题。这些问题似乎不具有可重现性,但有时会发生,因此尽管在EDT上运行了每个相关的调用,但这可能是一个线程问题。

我做错了什么?

public class MessageHandler {

private volatile static MessageHandler messageHandler = null;
private List<Message>messages = null;
private volatile WeakReference<MessagesPanelControl> view = null;

private final Object viewSynchronizationObject = new Object();

private MessageHandler() {
    messages = new ArrayList<Message>();
}

public static MessageHandler getInstance() {
    MessageHandler result = messageHandler;
    if (result == null) {
        synchronized (MessageHandler.class) {
            result = messageHandler;
            if (result == null)
                messageHandler = result = new MessageHandler();
        }
    }
    return result;
}


public void registerView(MessagesPanelControl view) {
    this.view = new WeakReference<MessagesPanelControl>(view);
}

public void addMessage(final Message message) {
        synchronized (viewSynchronizationObject) {
           messages.add(message);
           Collections.sort(messages);
           updateView();
        }
}

    private void updateView() {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            synchronized (viewSynchronizationObject) {
                if (view == null) {
                    return;
                }
                MessagesPanelControl mpc = view.get();
                if (mpc != null) {
                    mpc.updateView();
                } 
            }
        }
    });
}
}

在MainFrame类中,我在启动时进行了一次初始化:

MessagesPanelControl mp = new MessagesPanelControl();
MessageHandler.getInstance().registerView(mp);
LockPane messageBasicPane = new LockPane(this, mp);

然后在不同的线程中调用它来通过MessageHandler Singleton显示消息:

MessageHandler.getInstance().addMessage(Message.getSimpleMessage("Error", "Fatal error occured", Message.MessageIcon.ERROR));

我没有发布所有细节,但是所有必要的部分都是为了理解整个问题,希望它能让它更容易理解。

MessagePanelControl(mpc)是一个扩展JPanel的类。它的updateView() - 方法根据MessageHandler's消息列表创建消息控件,如按钮,标签和图标。最后,该方法向主框架发送Delegate类似命令,以显示包含JDialog的{​​{1}}。 总结它:

  • messageList.size()&gt; 0:为MessagePanelControl
  • 中列表中的每封邮件创建邮件面板
  • messageList.size()&gt; 0:显示带有MessageHandler
  • 的JDialog
  • messageList.size()&lt; = 0:用MessagePanelControl隐藏JDialog

    public void updateView(){     synchronized(viewMPCSynchronizationObject){         Utils.throwExceptionWhenNotOnEDT();

    MessagePanelControl

    }

大型机:

    JPanel messagesListPanel = new JPanel();
    scrollPane.setViewportView(messagesListPanel);
    scrollPane.setBorder(null);
    messagesListPanel.setLayout(new BoxLayout(messagesListPanel, BoxLayout.Y_AXIS));
    if (MessageHandler.getInstance().getMessages() != null &&  MessageHandler.getInstance().getMessages().size() > 0) {
                  [...]
                  //Create buttons, text icons... for each message
                  [...]
      SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MainFrame().showMessageBoard();
            }
        }); 
    } else {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MainFrame().closeMessageBoard();
            }
        });
    }
    repaint();
}

此行详细创建 //Show Messageboard public void showMessageBoard() { if (messageBasicPane != null) { messageBasicPane.setVisible(true); messageBasicPane.repaint(); } } [...] //Close Messageboard public void closeMessageBoard() { if (messageBasicPane != null) { messageBasicPane.setVisible(false); } }

JDialog

[...] public LockPane(JFrame parentFrame, JComponent componentToShow, Dimension paneSize, float opacity, ModalityType modality) { super(parentFrame, true); Utils.throwExceptionWhenNotOnEDT(); createDialog(paneSize, opacity, modality); if (componentToShow != null) { add(componentToShow); } pack(); } private void createDialog(Dimension paneSize, float opacity, ModalityType modality) { Utils.throwExceptionWhenNotOnEDT(); setUndecorated(true); setModalityType(modality); if (opacity < 1 && opacity >= 0) com.sun.awt.AWTUtilities.setWindowOpacity(this, opacity); setSize(paneSize); setPreferredSize(paneSize); setMaximumSize(paneSize); setBounds(0, 0, paneSize.width, paneSize.height); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); } [...] 的一个新观察是AWT-EventQueue没有被阻止,只有在有一段时间“等待”但没有阻塞的时候。另一个奇怪的事情是,有时我的Java VisualVM完全透明(不可见),有时候它的白色具有所需的不透明度。

1 个答案:

答案 0 :(得分:0)

在这个函数中,你实际上是在试图等待传递给Runnable的{​​{1}} SwingUtilities.invokeLaterinvokeLater提交给EDT来执行。您在viewSynchronizationObject上持有的锁将阻止EDT,如果它被其他应用程序线程锁定,这在您的代码中是显而易见的,因为您已在其他几个地方使用此变量。

private void updateView() {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            synchronized (viewSynchronizationObject) {
                if (view == null) {
                    return;
                }
                MessagesPanelControl mpc = view.get();
                if (mpc != null) {
                    mpc.updateView();
                } 
            }
        }


 },false);
}

我们永远不应阻止EDT执行它的任务,否则我们的应用程序会冻结。请阅读我的posted answer here for details,了解如何通过EDT和EventQueue执行Swing事件和渲染任务。

虽然我们不了解您的应用程序逻辑,但您可以从synchronized (viewSynchronizationObject) {}中删除invokeLater,而是可以将SwingUtilities.invokeLater(new Runnable() {}放在此同步块中:

synchronized (viewSynchronizationObject) 
{
   SwingUtilities.invokeLater(new Runnable() { /* your code */ } );
}