我有一个场景,我需要我的swing UI在两个不同的线程上运行。我有一台笔记本电脑,我将运行我的应用程序。单击时会出现一个按钮,演示文稿应从连接到我的笔记本电脑的另一个屏幕开始。
现在我做了一个类演示,它扩展了SwingWorker并从文件夹中读取图像并将其显示在屏幕上。
class Presenatation extends SwingWorker<Integer, Integer> {
@Override
protected Integer doInBackground() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
start(outputFolder, screenVO);/*Creates a JFrame to be displayed
on new screen and sets a JPanel to it. Reads the file images sets it into
JLabels every 2 seconds and updates it to Japnel*/
}
});
return null;
}
在我的start方法中,我有代码来读取图像并在UI上显示它们
我觉得这种方法是错误的,因为我的SwingWorker不应该在doInBackground()中调用invokeLater
从我的小知识,它应该是这样的:
@Override
protected Void doInBackground() throws Exception
{
return null;
}
@Override
protected void process(List<Integer> chunks
{
}
我无法决定哪个部分应放在哪里?
我有以下事情要做:
请帮助我!
答案 0 :(得分:0)
实际上,您不应该从doInbackground调用invokeLater,也不应该生成新线程。请注意,SwingWorker是一个Runnable,因此可以将其提交给Executor。
但是每次准备好显示新图像时都必须调用发布方法。在场景后面,SwingWorker将在Event Dispatch线程上调用其进程方法。
因此,您必须覆盖进程以执行实际的小部件更新。
答案 1 :(得分:0)
这个答案带着一丝盐,因为许多人不同意并说这是一种可怕的做法。然而,就我所见,没有人可以准确地说出为什么这是坏事。所以,在那之前,我坚持自己的观点:
invokeLater()
中拨打invokeAndWait()
和doInBackground()
是完全可以的。在那里,我说了。
我上个月问了basically the same question,从那时起我一直在尝试用process()
替换publish()
和invokeLater()
。在我的实验中,我没有遇到任何线程或同步问题。这种方法比publish()
和process()
容易得多。
我为什么这么说?
首先,通过调用invokeLater
,您将向EDT投放的任何代码。因此,没有合理的理由说明代码应该破解。
其次,process()
和publish()
的编写时考虑了非常非常具体的目标(即,为您提供一种发送表格式结果的方式,以便您可以更新JTable
或JList
实时)我从来没有,不是一次,使用process()
或publish()
的方式显然是写(我使用了很多表格数据)。为了让publish()
和process()
做我想做的事(90%的时间更新不确定的JProgressBar
,9%的时间更新确定的JProgressBar
),我通常最终会编写一些发布和处理数据的hackish方式。它令人困惑,难以阅读,难以管理变更请求。
然而,invokeLater()
内的doInBackground()
很容易阅读,而且我再也听不到有人说为什么这样做会不安全。而且,正如我在上面链接的问题中提到的情况一样,如果您需要暂停执行以获得用户反馈,我认为除了invokeAndWait()
之外没有任何其他选项。
我的诚实意见是publish()
和process()
写得不是很好。一旦你了解了细微差别,SwingWorker
就会很棒,但这些方法并不能满足大多数人使用SwingWorker
的需求以及他们需要更新用户的进度。至少,不是我的经验。
编辑:特别是关于你的情况,我会做的是:
为SwingWorker
创建一个构造函数并在那里初始化一个对话框,但要使其成为SwingWorker
的类成员,以便您可以更新它。例如:
class Task扩展了SwingWorker {
IndeterminateLoadingDialog ild;
public Task _Task() {
JDialog dialog = new JDialog(// parent frame);
ild = new IndeterminateLoadingDialog(this);
dialog.add(ild);
dialog.pack();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setLocationRelativeTo(dialog.getParent());
dialog.setVisible(true);
}
我的IndeterminateLoadingDialog
班级是JPanel
,只有JProgressBar
和JLabel
。我可以通过与JLabel
接口的方法更改文本。
无论如何,我总是初始化构造函数中SwingWorker
所需的任何GUI组件。只需确保从EDT调用new Task().execute()
。
如果我需要更新IndeterminateLoadingDialog
,我只会使用invokeLater()
直接更改文字。
此外,我使用done()
方法关闭对话框。
希望这会对你有所帮助。
答案 2 :(得分:0)
Swing(和所有其他GUI框架)不是多线程的(因为有很多有效的原因)。
因此,一个好的经验法则是从EDT主题 创建和操作任何GUI组件。有一些边缘情况(例如从一个线程创建一个新的JFrame)可能会起作用,但基本上从与EDT不同的线程对GUI做任何事情都是一个坏主意。
您不仅应该,而且必须从任何非EDT线程调用invokeLater或invokeAndWait。这是确保您的线程被挂起的唯一方法,并且提交的Runnable将从EDT线程的上下文执行。
基本上,你绝对不需要两个EDT线程。事实上,这是一个坏主意,你有一个键盘,一个鼠标创建一组UI事件,因此两个EDT线程没有用。
事实上,你甚至不需要一个swingworker来切换第二个显示器上的图片。 SwingWorkers非常适合从EDT线程卸载长时间运行的非gui操作(即执行需要20秒才能完成的数据库操作)。在你的情况下,加载一张新图片并不是火箭科学:)
执行以下操作:
实际上非常简单。考虑这个例子:
package a;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class PresentationStart extends JFrame {
public static void main(final String[] args) {
new PresentationStart();
}
public PresentationStart() {
super("Start here");
final JButton button=new JButton("Start");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
new PresentationView();
}
});
add(button);
pack();
setVisible(true);
}
}
观众:
package a;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class PresentationView extends JFrame {
public PresentationView() {
super("View");
final JLabel picture=new JLabel("Picture comes here");
add(picture);
pack();
setVisible(true);
final List<String> pictures=new ArrayList<String>();
pictures.add("http://storage3d.com/storage/2008.10/49d7c6aeed760176755a7570b55db587.jpg");
pictures.add("http://storage3d.com/storage/2008.10/49d7c6aeed760176755a7570b55db587.jpg");
pictures.add("http://www.alragdkw.com/wp-content/uploads/2016/01/fruit-Banans.jpg");
final Timer timer=new Timer(2000,new ActionListener() {
int index=0;
@Override
public void actionPerformed(final ActionEvent e) {
// Load a new picture
try {
picture.setIcon(new ImageIcon(ImageIO.read(new URL(pictures.get(index)))));
} catch (final Exception ex) {
ex.printStackTrace();
}
index++;
if (index>=pictures.size()) {
index=0;
}
}
});
timer.start();
}
}
答案 3 :(得分:-1)
将您的演示文稿作为独立的Java程序,并使用Runtime.exec()启动它。它将创建一个单独的窗口。