JDialog 不可见,组件可点击 [英] JDialog invisible, components clickable

查看:56
本文介绍了JDialog 不可见,组件可点击的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用在应用程序启动时设置的 JDialog 来多次显示消息.有时对话框及其控件是不可见的,但可以点击.

I'm using a JDialog instatiated at startup of the application, to show messages several times. Sometimes the dialog and its controls are invisible, but clickable.

JDialog 仅实例化一次,并在每次应显示消息时设置为可见的真",然后设置为可见的假",直到应显示下一条消息.

The JDialog is instantiated only once and set to visible 'true' each time a message should be shown and then set to visible 'false' till the next message should be shown.

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

To exlude multithreading related problems, i always use SwingUtilities.invokeLater(...) for ui calls, when a thread creates a message and shows the dialog.

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

Because its a huge project and my problem isn't related to any specific code, i don't post code but describe the problem. The problems seems not to be reproducible but happens sometimes, so it might be a threading problem despite running each related call on the 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 类中,我在启动时进行一次初始化:

In the MainFrame class i'm doing this initialization once at startup:

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

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

And then in different threads this is called to show a message via the MessageHandler Singleton:

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

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

I didn't post all details, but all necessary parts to understand the whole problme, hope it makes it more understandable.

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

The MessagePanelControl (mpc) is a class, that extends JPanel. Its updateView()-method creates the message controlls based on the MessageHandler's message list like buttons, labels and icons. Finally the method sends a Delegate like command to the main frame to show the JDialog containing the MessagePanelControl. Summarized it does:

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

public void updateView() {同步(viewMPCSynchronizationObject){Utils.throwExceptionWhenNotOnEDT();

public void updateView() { synchronized (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,详细说明:

This line creates the JDialog, in detail:

[...]
    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 是完全透明的(不可见),有时它是白色的,具有所需的不透明度.

A new observation with Java VisualVM is, that the AWT-EventQueue isn't blocked, only sometime there are small periods of 'wait' but nothing blocking. Another strange thing is, that sometimes my JDialog is fully transparent (invisible) and sometimes its white with the desired opacity.

推荐答案

在这个函数中,您实际上是在尝试等待传递给 SwingUtilities.invokeLaterRunnableinvokeLater 提交给 EDT 以执行.如果您在 viewSynchronizationObject 上持有的锁被其他应用程序线程锁定,则它会阻塞 EDT,这实际上从您的代码中可以看出,因为您在其他几个地方使用了这个变量.

In this function, you are essentially trying to await the Runnable passed to the SwingUtilities.invokeLater which invokeLater submits to the EDT to get executed. The lock that you are holding on viewSynchronizationObject will block EDT if it is locked by other application thread which is actually evident from your code as you have used this variable in several other places.

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 执行它的任务,否则我们的应用程序会冻结.请阅读我此处发布的答案以了解详情Swing 事件和渲染任务由 EDT 和 EventQueue 执行.

We should never block EDT from executing it's task other wise our application freezes. Please read my posted answer here for details on how the Swing event and rendering task is performed by EDT and EventQueue.

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

Although, your application logic is not known to us, you can remove the synchronized (viewSynchronizationObject) {} from invokeLater, instead you can put SwingUtilities.invokeLater(new Runnable() {} inside this synchronized block:

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

这篇关于JDialog 不可见,组件可点击的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆