繁体   English   中英

JDialog不可见,组件可单击

[英]JDialog invisible, components clickable

我使用的是在应用程序启动时设置为Instatiated的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消息列表(如按钮,标签和图标updateView()创建消息控件。 最后,该方法向主框架发送类似于Delegate命令,以显示包含MessagePanelControlJDialog 总结一下:

  • messageList.size()> 0:为MessageHandler中的列表中的每个消息创建消息面板
  • messageList.size()> 0:显示带有MessagePanelControl JDialog
  • messageList.size()<= 0:使用MessagePanelControl隐藏JDialog

    公共无效的updateView(){已同步(viewMPCSynchronizationObject){Utils.throwExceptionWhenNotOnEDT();

      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);
    }   
[...]

Java VisualVM一项新发现是,AWT-EventQueue没有被阻塞,只是在某个时间段内有很小的“等待”时间,而没有阻塞。 另一个奇怪的事情是,有时我的JDialog是完全透明的(不可见的),有时是白色的并且具有所需的不透明度。

在此函数中,您实际上是在等待传递给SwingUtilities.invokeLaterRunnable ,后者将invokeLater提交给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执行任务,否则应用程序将冻结。 请阅读我发布的答案,以了解有关EDT和EventQueue如何执行Swing事件和渲染任务的详细信息

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM