打开新对话框时,在加载时,在父shell上单击几次,显然新对话框无法正确显示。 请参阅以下示例:
最初我在2014年12月遇到了这个问题,当时我还报道了使用不同开发系统的房屋开发人员,然后我们的几个客户报告了同样的问题。
可以使用以下环境重现此行为:
- Windows版本:7 Pro 64位 - 6.1.7601
- Java版本:RE 1.8.0_121_b13
- SWT版本
- 3.8.2
- 4.6.2
- 4.7M6
- I20170319-2000
我只能在Windows 7上使用windows基本主题/设计/风格(不使用经典或航空)重现问题。 在Windows 10上它不可重复。
package test;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class Main {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = createShell(display);
createButton(shell);
shell.open();
eventLoop(display, shell);
display.dispose();
}
private static Shell createShell(Display display) {
final Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
shell.setSize(500, 200);
return shell;
}
private static void createButton(final Shell shell) {
final Button openDialog = new Button(shell, SWT.PUSH);
openDialog.setText("Click here to open Dialog ...");
openDialog.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
TestDialog inputDialog = new TestDialog(shell);
inputDialog.open();
}
});
}
private static void eventLoop(Display display, final Shell shell) {
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
class TestDialog extends Dialog {
public TestDialog(Shell parent) {
super(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.MAX | SWT.RESIZE);
setText("Dialog");
}
public void open() {
Shell shell = new Shell(getParent(), getStyle());
shell.setText(getText());
createContents(shell);
shell.pack();
initializeBounds(shell);
shell.open();
eventLoop(shell);
}
private void createContents(final Shell shell) {
shell.setLayout(new GridLayout(2, true));
Label label = new Label(shell, SWT.NONE);
label.setText("Some Label text ...");
final Text text = new Text(shell, SWT.BORDER);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
text.setLayoutData(data);
createCloseButton(shell);
/* time for the user to create the misbehavior */
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void createCloseButton(final Shell shell) {
Button closeButton = new Button(shell, SWT.PUSH);
closeButton.setText("Close");
GridData data = new GridData(GridData.FILL_HORIZONTAL);
closeButton.setLayoutData(data);
closeButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
shell.close();
}
});
shell.setDefaultButton(closeButton);
}
private void initializeBounds(Shell shell) {
Rectangle bounds = shell.getBounds();
Rectangle parentBounds = getParent().getBounds();
bounds.x = parentBounds.x;
bounds.y = parentBounds.y;
shell.setBounds(bounds);
}
private void eventLoop(Shell shell) {
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
当鼠标悬停某些UI元素(原来未正确绘制)时,您会注意到其中一些要绘制(例如表格行)。
在对话框打开后即使调用Shell.update()
或Shell.redraw()
也无法修复它。
在Windows性能选项中 - >视觉效果 - >禁用“在窗口和按钮上使用视觉样式”是我找到的唯一提供解决方法的选项, 这似乎与将设计/主题/风格改为经典相同。
最后,我有以下问题: 这是SWT还是Windows问题? Windows或Eclipse Bugzilla的错误条目中是否有相关主题? 是否有其他人遇到过同样的问题?请分享经验。 SWT或Windows中是否存在可能影响其外观并解决问题的设置?
答案 0 :(得分:1)
最后,我有以下问题:是SWT还是Windows问题?
都不是。正如其他人所提到的,你当然不应该将UI线程与任何长期运行的任务联系起来。这项工作属于后台主题。
关于使用后台线程,有几种方法可以解决这个问题,具体取决于您希望Dialog
的行为方式。
一个选项是启动后台线程,然后在任务完成时打开对话框。我个人并不关心这一点,因为当任务正在运行时,用户可能会认为没有任何事情发生。
另一种选择是打开对话框但显示"正在加载"消息或其他相关内容,以提供有意义的反馈,并让用户知道应用程序未被冻结(例如它在您的示例中的外观/响应方式)。
策略是:
如果您使用Executors
搜索一下,您应该找到一些更好的示例和详细说明如何使用它们。
这是一个简短的例子来说明可能的样子: (注意:这段代码肯定有一些问题,但为了简洁和说明我选择了一个稍微天真的解决方案。还有Java 8式的方式会更短,但是再次,这说明了使用后台线程背后的想法;相同的概念适用)
给定Callable
(如果您不需要返回值,则为Runnable
),
public class LongTask implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
您可以使用Executors
类创建线程池,然后使用ExecutorService
提交Callable
以执行。然后,使用Futures.addCallback()
,您可以注册一个回调,该回调将根据任务是成功还是失败执行两种方法之一。
final ExecutorService threadPool = Executors.newFixedThreadPool(1);
final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(threadPool);
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new FutureCallback(){...});
在这种情况下,我使用了Google Guava实现ListeningExecutorService
,在我看来,这使得事情变得更清晰,更简单。但是,如果你选择更多的Java 8&#34;你可能甚至不需要这个。方法的
至于回调,当任务成功时,我们会用结果更新Dialog
。如果失败,我们可以用一些东西来更新它以表明失败:
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
@Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
dialog.setStatus(result);
}
});
}
@Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
在这种情况下,我选择Callable
返回String
,因此FutureCallback
应该使用String
参数化。您可能想要使用您创建的其他类,这也可以正常工作。
请注意,我们使用Display.asyncExec()
方法确保更新UI的代码在UI线程上运行,因为回调可以在后台线程上执行。
就像我说的,这里仍然存在一些问题,包括在任务完成之前单击取消按钮时会发生什么等等。但希望这有助于说明处理长时间运行的后台任务而不阻止UI线程的方法
完整的示例代码:
public class DialogTaskExample {
private final Display display;
private final Shell shell;
private final ListeningExecutorService executorService;
public DialogTaskExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new GridLayout());
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
final Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
button.setText("Start");
button.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("synthetic-access")
@Override
public void widgetSelected(final SelectionEvent e) {
final MyDialog dialog = new MyDialog(shell);
dialog.setBlockOnOpen(false);
dialog.open();
dialog.setStatus("Doing stuff...");
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new DialogCallback(dialog));
}
});
}
public void run() {
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
executorService.shutdown();
display.dispose();
}
public static void main(final String... args) {
new DialogTaskExample().run();
}
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
@Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
dialog.setStatus(result);
}
});
}
@Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
public static class LongTask implements Callable<String> {
/**
* {@inheritDoc}
*/
@Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
public static class MyDialog extends Dialog {
private Composite baseComposite;
private Label label;
/**
* @param parentShell
*/
protected MyDialog(final Shell parentShell) {
super(parentShell);
}
/**
* {@inheritDoc}
*/
@Override
protected Control createDialogArea(final Composite parent) {
baseComposite = (Composite) super.createDialogArea(parent);
label = new Label(baseComposite, SWT.NONE);
return baseComposite;
}
public void setStatus(final String text) {
label.setText(text);
baseComposite.layout();
}
}
}
答案 1 :(得分:0)
代码似乎是直截了当的,只是你让主线程休眠了15秒,因此延迟了。如果不需要,请取消睡眠或将睡眠时间减少到5秒左右。