我在 Mac 上(使用 10.6.4、10.7.5 和 10.8.2 进行了测试),桌面已缩放。当我的 Java 应用程序同时使用 Swing 和 SWT 时,窗口在打开时就冻结了。该问题也可以通过 SWT 网页上提供的片段 337 重现(这里是一个链接)。
package org.eclipse.swt.snippets;
/*
* SWT_AWT example snippet: launch SWT from AWT and keep both active
*
* For a list of all SWT example snippets see
* http://www.eclipse.org/swt/snippets/
*/
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
public class Snippet337 {
public static void main(String args[]) {
final Display display = new Display();
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame mainFrame = new JFrame("Main Window");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout());
JButton launchBrowserButton = new JButton("Launch Browser");
launchBrowserButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFrame childFrame = new JFrame();
final Canvas canvas = new Canvas();
childFrame.setSize(850, 650);
childFrame.getContentPane().add(canvas);
childFrame.setVisible(true);
display.asyncExec(new Runnable() {
public void run() {
Shell shell = SWT_AWT.new_Shell(display, canvas);
shell.setSize(800, 600);
Browser browser = new Browser(shell, SWT.NONE);
browser.setLayoutData(new GridData(GridData.FILL_BOTH));
browser.setSize(800, 600);
browser.setUrl("http://www.eclipse.org");
shell.open();
}
});
}
});
mainPanel.add(new JTextField("a JTextField"));
mainPanel.add(launchBrowserButton);
mainFrame.getContentPane().add(mainPanel, BorderLayout.CENTER);
mainFrame.pack();
mainFrame.setVisible(true);
}
});
display.addListener(SWT.Close, new Listener() {
public void handleEvent(Event event) {
EventQueue.invokeLater(new Runnable() {
public void run() {
Frame[] frames = JFrame.getFrames();
for (int i = 0; i < frames.length; i++) {
frames[i].dispose();
}
}
});
}
});
while (!display.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
}
}
如果桌面未缩放,则应用程序可以正常启动并且没有问题。
阻塞的事件派发线程的堆栈如下所示:
CWindow._setVisible(long, boolean, boolean, int, boolean, int, int, boolean) line: not available [native method]
CFrame.setVisible(boolean) line: 111
CFrame(CComponent).show() line: 219
JFrame(Component).show() line: 1530
JFrame(Window).show() line: 871
JFrame(Component).show(boolean) line: 1563
JFrame(Component).setVisible(boolean) line: 1515
JFrame(Window).setVisible(boolean) line: 841
有谁知道如何解决这个问题?谢谢。
编辑-解决方法 1
如果我将 setVisible 包含在 asyncExec 中并且不使用 SWT_AWT 外壳,而是使用这样的普通外壳:
....
display.asyncExec(new Runnable() {
@Override
public void run() {
mainFrame.pack();
mainFrame.setVisible(true);
});
....
launchBrowserButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
display.asyncExec(new Runnable() {
public void run() {
Shell shell = new Shell(display);
shell.setSize(800, 600);
Browser browser = new Browser(shell, SWT.NONE);
...
}
});
}
});
它似乎适用于这个简单的示例,但在我的应用程序中仍然挂起在 EDQ 中调度的某些事件,因此不是解决方案....
解决方法 2
如果我替换 EventQueue 以在 Thread0 中调度事件,它也适用于这个小例子,它也成功地启动了我的复杂应用程序,但后来这个队列替换还有很多其他随机错误
...
EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
eventQueue.push(new MyEDQ());
...
class MyEQD extends EventQueue {
@Override
protected void dispatchEvent( AWTEvent arg0) {
final AWTEvent event = arg0;
A.display.asyncExec(new Runnable() {
@Override
public void run() {
MyEQD.super.dispatchEvent(event); }
});
}
}
}
...
所以仍然有人对如何更优雅地解决 Thread0 和 EventDispatchThread 之间的同步问题有任何想法,这样我就不会遇到其他问题?