我有一个父窗口,它将启动另一个窗口。当子窗口启动时,我希望它像模态对话框一样显示。
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()
方法。
-
JFrame
不能是模态,用JDialog
代替 -
不要创建一堆
JFrame
s,使用JDialog
代替 -
只创建一个
JDialog
作为局部变量,在JDialog.setVisible(false)
被调用之前,JDialog.getContentPane.removeAll()
重新使用这个容器进行另一个动作 -
注意
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
}
}