如何实现模态对话框的效果



我有一个父窗口,它将启动另一个窗口。当子窗口启动时,我希望它像模态对话框一样显示。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ModalDialogTest {
    public void createUI(){
        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        JPanel mainPanel = new JPanel();
        mainPanel.setBorder(BorderFactory.createEmptyBorder(200, 200, 200, 200));
        JButton openButton = new JButton("Open a frame");
        openButton.addActionListener(new ActionListener() { 
            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub
                AnotherWindow anotherWindow = new AnotherWindow();
                anotherWindow.createUI();
            }
        });
        mainPanel.add(openButton,BorderLayout.CENTER);
        frame.add(mainPanel,BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    public static void main(String[] args) {
        ModalDialogTest modelDialogTest = new ModalDialogTest();
        modelDialogTest.createUI();
    }
    class AnotherWindow{
        public void createUI(){
            JFrame frame = new JFrame("Dialog");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setResizable(false);
            JPanel mainPanel = new JPanel();
            mainPanel.setBorder(BorderFactory.createEmptyBorder(100, 100, 100, 100));
            JLabel label = new JLabel("I want to be a modal dialog");
            mainPanel.add(label,BorderLayout.CENTER);
            frame.add(mainPanel,BorderLayout.CENTER);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    }
}

上面的演示描述了我的应用程序的过程和架构是一样的。那么解决方案是什么呢?

不能设置JFrame的模态;第二个窗口必须是JDialog。因此,将第二个JFrame更改为JDialog,并对其使用setModalityType()方法。

  1. JFrame不能是模态,用JDialog代替

  2. 不要创建一堆JFrame s,使用JDialog代替

  3. 只创建一个JDialog作为局部变量,在JDialog.setVisible(false)被调用之前,JDialog.getContentPane.removeAll()重新使用这个容器进行另一个动作

  4. 注意Top-Level Containers永远不会被GC'ed,所有的新实例都会增加使用的JVM内存,关于这里的更多细节

可以使用Frame模式,但是应该使用JDialog作为标准选择。这里是一个例子,我发现使一个框架模态(代码是Java 1.4,但也应该为实际的Java版本工作)。

    static class EventPump implements InvocationHandler {
        Frame frame;
        public EventPump(Frame frame) {
            this.frame = frame;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return frame.isShowing() ? Boolean.TRUE : Boolean.FALSE;
        }
        // when the reflection calls in this method has to be
        // replaced once Sun provides a public API to pump events.
        public void start() throws Exception {
            final Class clazz = Class.forName("java.awt.Conditional");
            final Object conditional = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz}, this);
            final Method pumpMethod = Class.forName("java.awt.EventDispatchThread").getDeclaredMethod("pumpEvents", new Class[] {clazz});
            pumpMethod.setAccessible(true);
            pumpMethod.invoke(Thread.currentThread(), new Object[] {conditional});
        }
    }
    // show the given frame as modal to the specified owner.
    // NOTE: this method returns only after the modal frame is closed.
    public static void showAsModal(final Frame frame, final Frame owner) {
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                owner.setEnabled(false);
            }
            @Override
            public void windowClosed(WindowEvent e) {
                owner.setEnabled(true);
                frame.removeWindowListener(this);
            }
        });
        owner.addWindowListener(new WindowAdapter() {
            @Override
            public void windowActivated(WindowEvent e) {
                if (frame.isShowing()) {
                    frame.setExtendedState(JFrame.NORMAL);
                    frame.toFront();
                } else {
                    owner.removeWindowListener(this);
                }
            }
        });
        frame.setVisible(true);
        try {
            new EventPump(frame).start();
        } catch (final Throwable throwable) {
            throw new RuntimeException(throwable); // NOPMD
        }
    }

最新更新