当框架不可见时,Java不会处理对话框

时间:2014-08-28 20:49:14

标签: java swing modal-dialog jframe dispose

使用多个框架时,我遇到隐藏组件未正确处理的问题。

简而言之,我无法处理其父级是隐藏框架的模态对话框。

例如:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;

public class MultipleFrameTest {
    public static void main(String[] args) {
        TestFrame   test   = new TestFrame();
        FrameTester tester = new FrameTester(test);

        tester.setVisible(true);
    }

    private static class TestFrame extends JFrame {
        JDialog dialog;
        java.util.Timer timer;

        public TestFrame() {
            super("Test Frame");

            this.dialog = null;
            this.timer  = new java.util.Timer("Frame Timer");

            fillFrame();
            pack();
        }

        private void fillFrame() {
            JButton dialogButton = new JButton("Launch Model Dialog");
            dialogButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    JOptionPane pane = new JOptionPane("Wait for 2 seconds",
                        JOptionPane.QUESTION_MESSAGE,
                        JOptionPane.OK_CANCEL_OPTION);
                    dialog = pane.createDialog(TestFrame.this, "Question");

                    timer.schedule(new TimerTask() {
                        public void run() {
                            SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                    TestFrame.this.setVisible(false);

                                    if (dialog != null) {
                                        dialog.setVisible(false);
                                        dialog.dispose();
                                        dialog = null;
                                    }
                                }
                            });
                        }
                    }, 2 * 1000);

                    dialog.setVisible(true);
                }
            });

            JPanel panel = new JPanel();
            panel.add(dialogButton);

            add(panel);
        }
    }    

    private static class FrameTester extends JFrame {
        JFrame frame;

        public FrameTester(JFrame frame) {
            super("Frame Tester");

            this.frame = frame;

            fillFrame();
            pack();
        }

        private void fillFrame() {
            JButton toggleButton = new JButton("Toggle Frame Visibility");
            toggleButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    frame.setVisible(!frame.isVisible());
                }
            });

            JPanel panel = new JPanel();
            panel.add(toggleButton);

            add(panel);
        }
    }
}

运行此示例:

  1. 点击"切换框架可见性"按钮。这将显示TestFrame
  2. 点击"启动模态对话框"按钮。这会弹出JOptionPane
  3. 等待2秒钟,TimerTask隐藏TestFramedispose() JOptionPane
  4. 点击"切换框架可见性按钮"。 TestFrame将变为可见,JOptionPane将被附加。
  5. 我知道我可以通过在隐藏JOptionPane隐藏TestFrame之前解决此问题:

    - TestFrame.this.setVisible(false);
    
      if (dialog != null) {
         dialog.setVisible(false);
         dialog.dispose();
         dialog = null;
      }
    
    + TestFrame.this.setVisible(false);
    

    有人知道为什么会这样吗?我希望模态对话框即使在被处理时被隐藏也会消失。

1 个答案:

答案 0 :(得分:4)

虽然您应该真正考虑对The Use of Multiple JFrames, Good/Bad Practice?的垃圾邮件评论,但以下是您看到此行为的原因的解释。

这是因为您在对话框之前隐藏了拥有窗口。执行此操作时,拥有的对话框将标记为showWithParent,并且由于该标志,框架上的下一次setVisible(true)调用将自动触发所拥有的对话框的显示。正如您在问题中所述,避免这种情况的唯一方法是首先隐藏拥有的对话框,然后隐藏拥有的窗口。

以下是Window.hide()方法的摘录:

    synchronized(ownedWindowList) {
        for (int i = 0; i < ownedWindowList.size(); i++) {
            Window child = ownedWindowList.elementAt(i).get();
            if ((child != null) && child.visible) {
                child.hide();
                child.showWithParent = true; // See here the flag set to true
            }
        }
    }

这里是Window.show()方法的相应摘录:

       for (int i = 0; i < ownedWindowList.size(); i++) {
            Window child = ownedWindowList.elementAt(i).get();
            if ((child != null) && child.showWithParent) { // Here theh flag is checked
                child.show();
                child.showWithParent = false; // flag is then reset
            }       // endif
        }

顺便说一句,您可以使用javax.swing.TimersetRepeats(false)),而不是使用此复杂的计时器/ TimerTask / invokeLater结构。 Swing计时器始终在Event Dispatching Thread上运行。